001package 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
023import org.gwtbootstrap3.client.ui.base.HasActive;
024import org.gwtbootstrap3.client.ui.base.HasIcon;
025import org.gwtbootstrap3.client.ui.base.HasIconPosition;
026import org.gwtbootstrap3.client.ui.base.HasSize;
027import org.gwtbootstrap3.client.ui.base.HasType;
028import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
029import org.gwtbootstrap3.client.ui.base.mixin.ActiveMixin;
030import org.gwtbootstrap3.client.ui.constants.ButtonSize;
031import org.gwtbootstrap3.client.ui.constants.ButtonType;
032import org.gwtbootstrap3.client.ui.constants.IconFlip;
033import org.gwtbootstrap3.client.ui.constants.IconPosition;
034import org.gwtbootstrap3.client.ui.constants.IconRotate;
035import org.gwtbootstrap3.client.ui.constants.IconSize;
036import org.gwtbootstrap3.client.ui.constants.IconType;
037import org.gwtbootstrap3.client.ui.constants.Styles;
038
039import com.google.gwt.dom.client.Document;
040import com.google.gwt.dom.client.InputElement;
041import com.google.gwt.event.dom.client.ClickEvent;
042import com.google.gwt.event.dom.client.ClickHandler;
043import com.google.gwt.event.logical.shared.ValueChangeEvent;
044import com.google.gwt.i18n.client.HasDirection.Direction;
045import com.google.gwt.i18n.shared.DirectionEstimator;
046import com.google.gwt.safehtml.shared.SafeHtml;
047import com.google.gwt.user.client.DOM;
048import 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 */
058public 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    /** {@inheritDoc} */
302    @Override
303    public void setIconInverse(final boolean iconInverse) {
304        getActualIcon().setInverse(iconInverse);
305    }
306
307    /** {@inheritDoc} */
308    @Override
309    public boolean isIconInverse() {
310        return getActualIcon().isInverse();
311    }
312
313    @Override
314    public void setIconSpin(boolean iconSpin) {
315        getActualIcon().setSpin(iconSpin);
316    }
317
318    @Override
319    public boolean isIconSpin() {
320        return getActualIcon().isSpin();
321    }
322
323    @Override
324    public void setIconPulse(boolean iconPulse) {
325        getActualIcon().setPulse(iconPulse);
326    }
327
328    @Override
329    public boolean isIconPulse() {
330        return getActualIcon().isPulse();
331    }
332
333    @Override
334    public void setIconFixedWidth(boolean iconFixedWidth) {
335        getActualIcon().setFixedWidth(iconFixedWidth);
336    }
337
338    @Override
339    public boolean isIconFixedWidth() {
340        return getActualIcon().isFixedWidth();
341    }
342
343    private Icon getActualIcon() {
344        if (icon == null) {
345            icon = new Icon();
346            render();
347        }
348        return icon;
349    }
350
351    private void render() {
352        if (iconPosition == IconPosition.LEFT) {
353            getElement().insertAfter(icon.getElement(), inputElem);
354        } else {
355            getElement().insertAfter(icon.getElement(), null);
356        }
357    }
358
359}