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.user.client.DOM;
048    import com.google.gwt.user.client.Event;
049    
050    /**
051     * Button representing a checkbox used within a {@link ButtonGroup} that has
052     * toggle set to {@code Toogle.BUTTONS}.
053     * <p/>
054     * If you are looking for a classic checkbox see {@link CheckBox}.
055     *
056     * @author Sven Jacobs
057     */
058    public class CheckBoxButton extends CheckBox implements HasActive,
059            HasType<ButtonType>, HasSize<ButtonSize>, HasIcon, HasIconPosition {
060    
061        private final ActiveMixin<CheckBoxButton> activeMixin = new ActiveMixin<CheckBoxButton>(this);
062    
063        private IconPosition iconPosition = IconPosition.LEFT;
064        private Icon icon;
065    
066        /**
067         * Creates a check box button with the specified text label.
068         * 
069         * @param label
070         *            the check box's label
071         */
072        public CheckBoxButton(SafeHtml label) {
073            this(label.asString(), true);
074        }
075    
076        /**
077         * Creates a check box button with the specified text label.
078         * 
079         * @param label
080         *            the check box's label
081         * @param dir
082         *            the text's direction. Note that {@code DEFAULT} means
083         *            direction should be inherited from the widget's parent
084         *            element.
085         */
086        public CheckBoxButton(SafeHtml label, Direction dir) {
087            this();
088            setHTML(label, dir);
089        }
090    
091        /**
092         * Creates a check box button with the specified text label.
093         * 
094         * @param label
095         *            the check box's label
096         * @param directionEstimator
097         *            A DirectionEstimator object used for automatic direction
098         *            adjustment. For convenience,
099         *            {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
100         */
101        public CheckBoxButton(SafeHtml label, DirectionEstimator directionEstimator) {
102            this();
103            setDirectionEstimator(directionEstimator);
104            setHTML(label.asString());
105        }
106    
107        /**
108         * Creates a check box button with the specified text label.
109         * 
110         * @param label
111         *            the check box's label
112         */
113        public CheckBoxButton(String label) {
114            this();
115            setText(label);
116        }
117    
118        /**
119         * Creates a check box button with the specified text label.
120         * 
121         * @param label
122         *            the check box's label
123         * @param dir
124         *            the text's direction. Note that {@code DEFAULT} means
125         *            direction should be inherited from the widget's parent
126         *            element.
127         */
128        public CheckBoxButton(String label, Direction dir) {
129            this();
130            setText(label, dir);
131        }
132    
133        /**
134         * Creates a label with the specified text and a default direction
135         * estimator.
136         * 
137         * @param label
138         *            the check box's label
139         * @param directionEstimator
140         *            A DirectionEstimator object used for automatic direction
141         *            adjustment. For convenience,
142         *            {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
143         */
144        public CheckBoxButton(String label, DirectionEstimator directionEstimator) {
145            this();
146            setDirectionEstimator(directionEstimator);
147            setText(label);
148        }
149    
150        /**
151         * Creates a check box button with the specified text label.
152         * 
153         * @param label
154         *            the check box's label
155         * @param asHTML
156         *            <code>true</code> to treat the specified label as html
157         */
158        public CheckBoxButton(String label, boolean asHTML) {
159            this();
160            if (asHTML) {
161                setHTML(label);
162            } else {
163                setText(label);
164            }
165        }
166    
167        public CheckBoxButton() {
168            this(Document.get().createCheckInputElement());
169        }
170    
171        protected CheckBoxButton(InputElement element) {
172            super(DOM.createLabel(), element);
173    
174            setStyleName(Styles.BTN);
175            setType(ButtonType.DEFAULT);
176    
177            getElement().appendChild(inputElem);
178            getElement().appendChild(Document.get().createTextNode(" "));
179            getElement().appendChild(labelElem);
180            getElement().appendChild(Document.get().createTextNode(" "));
181        }
182    
183        @Override
184        protected void ensureDomEventHandlers() {
185            // Use a ClickHandler since Bootstrap's jQuery does not trigger native
186            // change events:
187            // http://learn.jquery.com/events/triggering-event-handlers/
188            addClickHandler(new ClickHandler() {
189    
190                @Override
191                public void onClick(ClickEvent event) {
192                    ValueChangeEvent.fire(CheckBoxButton.this, getValue());
193                }
194    
195            });
196        }
197    
198        @Override
199        public void sinkEvents(int eventBitsToAdd) {
200            // Sink on the actual element because that's what gets clicked
201            if (isOrWasAttached()) {
202                Event.sinkEvents(getElement(),
203                        eventBitsToAdd | Event.getEventsSunk(getElement()));
204            } else {
205                super.sinkEvents(eventBitsToAdd);
206            }
207        }
208    
209        @Override
210        public void setSize(ButtonSize size) {
211            StyleHelper.addUniqueEnumStyleName(this, ButtonSize.class, size);
212        }
213    
214        @Override
215        public ButtonSize getSize() {
216            return ButtonSize.fromStyleName(getStyleName());
217        }
218    
219        @Override
220        public void setType(ButtonType type) {
221            StyleHelper.addUniqueEnumStyleName(this, ButtonType.class, type);
222        }
223    
224        @Override
225        public ButtonType getType() {
226            return ButtonType.fromStyleName(getStyleName());
227        }
228    
229        @Override
230        public void setActive(boolean active) {
231            setValue(active);
232            activeMixin.setActive(active);
233        }
234    
235        @Override
236        public boolean isActive() {
237            return activeMixin.isActive();
238        }
239    
240        @Override
241        public void setIconPosition(IconPosition iconPosition) {
242            this.iconPosition = iconPosition;
243            render();
244        }
245    
246        @Override
247        public IconPosition getIconPosition() {
248            return iconPosition;
249        }
250    
251        @Override
252        public void setIcon(IconType iconType) {
253            getActualIcon().setType(iconType);
254        }
255    
256        @Override
257        public IconType getIcon() {
258            return getActualIcon().getType();
259        }
260    
261        @Override
262        public void setIconSize(IconSize iconSize) {
263            getActualIcon().setSize(iconSize);
264        }
265    
266        @Override
267        public IconSize getIconSize() {
268            return getActualIcon().getSize();
269        }
270    
271        @Override
272        public void setIconFlip(IconFlip iconFlip) {
273            getActualIcon().setFlip(iconFlip);
274        }
275    
276        @Override
277        public IconFlip getIconFlip() {
278            return getActualIcon().getFlip();
279        }
280    
281        @Override
282        public void setIconRotate(IconRotate iconRotate) {
283            getActualIcon().setRotate(iconRotate);
284        }
285    
286        @Override
287        public IconRotate getIconRotate() {
288            return getActualIcon().getRotate();
289        }
290    
291        @Override
292        public void setIconBordered(boolean iconBordered) {
293            getActualIcon().setBorder(iconBordered);
294        }
295    
296        @Override
297        public boolean isIconBordered() {
298            return getActualIcon().isBorder();
299        }
300    
301        @Override
302        public void setIconMuted(boolean iconMuted) {
303            getActualIcon().setMuted(iconMuted);
304        }
305    
306        @Override
307        public boolean isIconMuted() {
308            return getActualIcon().isMuted();
309        }
310    
311        @Override
312        public void setIconLight(boolean iconLight) {
313            getActualIcon().setLight(iconLight);
314        }
315    
316        @Override
317        public boolean isIconLight() {
318            return getActualIcon().isLight();
319        }
320    
321        @Override
322        public void setIconSpin(boolean iconSpin) {
323            getActualIcon().setSpin(iconSpin);
324        }
325    
326        @Override
327        public boolean isIconSpin() {
328            return getActualIcon().isSpin();
329        }
330    
331        @Override
332        public void setIconFixedWidth(boolean iconFixedWidth) {
333            getActualIcon().setFixedWidth(iconFixedWidth);
334        }
335    
336        @Override
337        public boolean isIconFixedWidth() {
338            return getActualIcon().isFixedWidth();
339        }
340    
341        private Icon getActualIcon() {
342            if (icon == null) {
343                icon = new Icon();
344                render();
345            }
346            return icon;
347        }
348    
349        private void render() {
350            if (iconPosition == IconPosition.LEFT) {
351                getElement().insertAfter(icon.getElement(), inputElem);
352            } else {
353                getElement().insertAfter(icon.getElement(), null);
354            }
355        }
356    
357    }