001package org.gwtbootstrap3.extras.toggleswitch.client.ui.base;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 - 2016 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.Icon;
024import org.gwtbootstrap3.client.ui.base.HasId;
025import org.gwtbootstrap3.client.ui.base.HasReadOnly;
026import org.gwtbootstrap3.client.ui.base.HasResponsiveness;
027import org.gwtbootstrap3.client.ui.base.HasSize;
028import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
029import org.gwtbootstrap3.client.ui.base.mixin.AttributeMixin;
030import org.gwtbootstrap3.client.ui.base.mixin.IdMixin;
031import org.gwtbootstrap3.client.ui.constants.DeviceSize;
032import org.gwtbootstrap3.client.ui.constants.IconSize;
033import org.gwtbootstrap3.client.ui.constants.IconType;
034import org.gwtbootstrap3.extras.toggleswitch.client.ui.base.constants.ColorType;
035import org.gwtbootstrap3.extras.toggleswitch.client.ui.base.constants.SizeType;
036
037import com.google.gwt.dom.client.Element;
038import com.google.gwt.dom.client.InputElement;
039import com.google.gwt.editor.client.IsEditor;
040import com.google.gwt.editor.client.LeafValueEditor;
041import com.google.gwt.editor.client.adapters.TakesValueEditor;
042import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
043import com.google.gwt.event.logical.shared.ValueChangeEvent;
044import com.google.gwt.event.logical.shared.ValueChangeHandler;
045import com.google.gwt.event.shared.HandlerRegistration;
046import com.google.gwt.user.client.ui.HasEnabled;
047import com.google.gwt.user.client.ui.HasName;
048import com.google.gwt.user.client.ui.HasValue;
049import com.google.gwt.user.client.ui.HasVisibility;
050import com.google.gwt.user.client.ui.Widget;
051
052/**
053 * Original source from http://www.bootstrap-switch.org/
054 *
055 * @author Grant Slender
056 * @author Steven Jardine
057 */
058public class ToggleSwitchBase extends Widget implements HasSize<SizeType>, HasValue<Boolean>, HasValueChangeHandlers<Boolean>,
059        HasEnabled, HasVisibility, HasId, HasName, HasReadOnly, HasResponsiveness, IsEditor<LeafValueEditor<Boolean>> {
060
061    private final InputElement element;
062    private SizeType size = SizeType.REGULAR;
063    private ColorType onColor = ColorType.DEFAULT;
064    private ColorType offColor = ColorType.PRIMARY;
065    private final IdMixin<ToggleSwitchBase> idMixin = new IdMixin<ToggleSwitchBase>(this);
066    private final AttributeMixin<ToggleSwitchBase> attributeMixin = new AttributeMixin<ToggleSwitchBase>(this);
067    private LeafValueEditor<Boolean> editor;
068
069    protected ToggleSwitchBase(InputElement element) {
070        this.element = element;
071        setElement(element);
072    }
073
074    @Override
075    protected void onLoad() {
076        super.onLoad();
077        switchInit(getElement());
078    }
079
080    @Override
081    protected void onUnload() {
082        super.onUnload();
083        switchDestroy(getElement());
084    }
085
086    @Override
087    public void setVisibleOn(final DeviceSize deviceSize) {
088        StyleHelper.setVisibleOn(this, deviceSize);
089    }
090
091    @Override
092    public void setHiddenOn(final DeviceSize deviceSize) {
093        StyleHelper.setHiddenOn(this, deviceSize);
094    }
095
096    @Override
097    public void setId(final String id) {
098        idMixin.setId(id);
099    }
100
101    @Override
102    public String getId() {
103        return idMixin.getId();
104    }
105
106    @Override
107    public void setName(String name) {
108        element.setName(name);
109    }
110
111    @Override
112    public String getName() {
113        return element.getName();
114    }
115
116    @Override
117    public boolean isEnabled() {
118        return !getBooleanAttribute(Option.DISABLED);
119    }
120
121    @Override
122    public void setEnabled(final boolean enabled) {
123        updateSwitch(Option.DISABLED, !enabled);
124    }
125
126    @Override
127    public SizeType getSize() {
128        return size;
129    }
130
131    @Override
132    public void setSize(final SizeType size) {
133        this.size = size;
134        updateSwitch(Option.SIZE, size.getType());
135    }
136
137    public ColorType getOnColor() {
138        return onColor;
139    }
140
141    public void setOnColor(final ColorType onColor) {
142        this.onColor = onColor;
143        updateSwitch(Option.ON_COLOR, onColor.getType());
144    }
145
146    public ColorType getOffColor() {
147        return offColor;
148    }
149
150    public void setOffColor(final ColorType offColor) {
151        this.offColor = offColor;
152        updateSwitch(Option.OFF_COLOR, offColor.getType());
153    }
154
155    public boolean isAnimate() {
156        return getBooleanAttribute(Option.ANIMATE);
157    }
158
159    public void setAnimate(final boolean animate) {
160        updateSwitch(Option.ANIMATE, animate);
161    }
162
163    public String getOnText() {
164        return getStringAttribute(Option.ON_TEXT);
165    }
166
167    public void setOnText(final String onText) {
168        updateSwitch(Option.ON_TEXT, onText);
169    }
170
171    public void setOnIcon(final IconType iconType) {
172        String text = createIconHtml(iconType);
173        setOnText(text);
174    }
175
176    public String getOffText() {
177        return getStringAttribute(Option.OFF_TEXT);
178    }
179
180    public void setOffText(final String offText) {
181        updateSwitch(Option.OFF_TEXT, offText);
182    }
183
184    public void setOffIcon(final IconType iconType) {
185        String text = createIconHtml(iconType);
186        setOffText(text);
187    }
188
189    public String getLabelText() {
190        return getStringAttribute(Option.LABEL_TEXT);
191    }
192
193    public void setLabelText(final String labelText) {
194        updateSwitch(Option.LABEL_TEXT, labelText);
195    }
196
197    @Override
198    public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<Boolean> handler) {
199        return addHandler(handler, ValueChangeEvent.getType());
200    }
201
202    @Override
203    public Boolean getValue() {
204        if (isAttached()) {
205            return switchState(getElement());
206        }
207        return element.isChecked();
208    }
209
210    @Override
211    public void setValue(final Boolean value) {
212        setValue(value, false);
213    }
214
215    @Override
216    public void setValue(final Boolean value, final boolean fireEvents) {
217        Boolean oldValue = getValue();
218        if (isAttached()) {
219            switchState(getElement(), value, true);
220        } else {
221            element.setChecked(value);
222        }
223        if (fireEvents) {
224            ValueChangeEvent.fireIfNotEqual(ToggleSwitchBase.this, oldValue, value);
225        }
226    }
227
228    public void onChange(final boolean value) {
229        ValueChangeEvent.fire(this, value);
230    }
231
232    @Override
233    public LeafValueEditor<Boolean> asEditor() {
234        if (editor == null) {
235            editor = TakesValueEditor.of(this);
236        }
237        return editor;
238    }
239
240    @Override
241    public boolean isReadOnly() {
242        return getBooleanAttribute(Option.READONLY);
243    }
244
245    @Override
246    public void setReadOnly(boolean readOnly) {
247        updateSwitch(Option.READONLY, readOnly);
248    }
249
250    public boolean isIndeterminate() {
251        return getBooleanAttribute(Option.INDETERMINATE);
252    }
253
254    /**
255     * Indeterminate state.
256     */
257    public void setIndeterminate(boolean indeterminate) {
258        updateSwitch(Option.INDETERMINATE, indeterminate);
259    }
260
261    public boolean isInverse() {
262        return getBooleanAttribute(Option.INVERSE);
263    }
264
265    /**
266     * Inverse switch direction.
267     */
268    public void setInverse(boolean inverse) {
269        updateSwitch(Option.INVERSE, inverse);
270    }
271
272    public boolean isRadioAllOff() {
273        return getBooleanAttribute(Option.RADIO_ALL_OFF);
274    }
275
276    /**
277     * Allow this radio button to be unchecked by the user.
278     */
279    public void setRadioAllOff(boolean radioAllOff) {
280        updateSwitch(Option.RADIO_ALL_OFF, radioAllOff);
281    }
282
283    /**
284     * Sets the handle's width.
285     * @param labelWidth - set to "auto" (default) for automatic sizing, integer otherwise
286     */
287    public void setHandleWidth(String handleWidth) {
288        updateSwitch(Option.HANDLE_WIDTH, handleWidth);
289    }
290
291    /**
292     * Sets the label's width (the space between handles).
293     * @param labelWidth - set to "auto" (default) for automatic sizing, integer otherwise
294     */
295    public void setLabelWidth(String labelWidth) {
296        updateSwitch(Option.LABEL_WIDTH, labelWidth);
297    }
298
299    @Override
300    public void setVisible(boolean visible) {
301        if (isAttached()) {
302            setVisible(getElement().getParentElement().getParentElement(), visible);
303        } else {
304            super.setVisible(visible);
305        }
306    }
307
308    @Override
309    public boolean isVisible() {
310        if (isAttached()) {
311            return isVisible(getElement().getParentElement().getParentElement());
312        }
313        return super.isVisible();
314    }
315
316    private String createIconHtml(IconType iconType) {
317        // Fix incorrect handle width when using icons
318        setHandleWidth("30");
319        final Icon icon = new Icon(iconType);
320        icon.setSize(IconSize.LARGE);
321        return icon.getElement().getString();
322    }
323
324    private void updateSwitch(Option option, String value) {
325        if (isAttached()) {
326            switchCmd(getElement(), option.getCommand(), value);
327        } else {
328            attributeMixin.setAttribute(option.getAttribute(), value);
329        }
330    }
331
332    private void updateSwitch(Option option, boolean value) {
333        if (isAttached()) {
334            switchCmd(getElement(), option.getCommand(), value);
335        } else {
336            attributeMixin.setAttribute(option.getAttribute(), Boolean.toString(value));
337        }
338    }
339
340    private String getStringAttribute(Option option) {
341        if (isAttached()) {
342            return getCommandStringValue(getElement(), option.getCommand());
343        } else {
344            return attributeMixin.getAttribute(option.getAttribute());
345        }
346    }
347
348    private boolean getBooleanAttribute(Option option) {
349        if (isAttached()) {
350            return getCommandBooleanValue(getElement(), option.getCommand());
351        } else {
352            String value = attributeMixin.getAttribute(option.getAttribute());
353            if (value != null && !value.isEmpty()) {
354                return Boolean.valueOf(value);
355            } else {
356                return false;
357            }
358        }
359    }
360
361    private native void switchInit(Element e) /*-{
362        $wnd.jQuery(e).bootstrapSwitch();
363
364        var me = this;
365        $wnd.jQuery(e).on('switchChange.bootstrapSwitch', function (em, state) {
366            me.@org.gwtbootstrap3.extras.toggleswitch.client.ui.base.ToggleSwitchBase::onChange(Z)(state);
367        });
368    }-*/;
369
370    private native void switchDestroy(Element e) /*-{
371        $wnd.jQuery(e).off('switchChange.bootstrapSwitch');
372        $wnd.jQuery(e).bootstrapSwitch('destroy');
373    }-*/;
374
375    private native void switchCmd(Element e, String cmd, String value) /*-{
376        $wnd.jQuery(e).bootstrapSwitch(cmd, value);
377    }-*/;
378
379    private native void switchCmd(Element e, String cmd, boolean value) /*-{
380        $wnd.jQuery(e).bootstrapSwitch(cmd, value);
381    }-*/;
382
383    private native String getCommandStringValue(Element e, String cmd) /*-{
384        return $wnd.jQuery(e).bootstrapSwitch(cmd);
385    }-*/;
386
387    private native boolean getCommandBooleanValue(Element e, String cmd) /*-{
388        return $wnd.jQuery(e).bootstrapSwitch(cmd);
389    }-*/;
390
391    private native void switchState(Element e, boolean value, boolean skip) /*-{
392        $wnd.jQuery(e).bootstrapSwitch('state', value, skip);
393    }-*/;
394
395    private native boolean switchState(Element e) /*-{
396        return $wnd.jQuery(e).bootstrapSwitch('state');
397    }-*/;
398}