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 */
016package com.github.gwtbootstrap.client.ui;
017
018import com.github.gwtbootstrap.client.ui.base.*;
019import com.github.gwtbootstrap.client.ui.constants.ButtonType;
020import com.github.gwtbootstrap.client.ui.constants.Constants;
021import com.github.gwtbootstrap.client.ui.constants.DismissType;
022import com.github.gwtbootstrap.client.ui.constants.IconType;
023import com.github.gwtbootstrap.client.ui.resources.ButtonSize;
024import com.google.gwt.dom.client.Document;
025import com.google.gwt.dom.client.Element;
026import com.google.gwt.dom.client.NativeEvent;
027import com.google.gwt.event.dom.client.*;
028import com.google.gwt.event.shared.HandlerRegistration;
029import com.google.gwt.user.client.ui.HasEnabled;
030
031//@formatter:off
032/**
033 * Button with optional icon.
034 *
035 * <p>
036 * <h3>UiBinder Usage:</h3>
037 *
038 * <pre>
039 * {@code <b:Button icon="TRASH" type="ERROR" toggle="true" loadingText="I'm loading..." completeText="Oh hoh, I completed the action!">Delete</b:Button>}
040 * </pre>
041 *
042 * All arguments are optional.
043 * </p>
044 *
045 * @since 2.0.4.0
046 *
047 * @author Carlos Alexandro Becker
048 * @author Dominik Mayer
049 * @author Sven Jacobs
050 *
051 * @see <a
052 *      href="http://twitter.github.com/bootstrap/base-css.html#buttons">Bootstrap
053 *      documentation</a>
054 * @see ButtonGroup
055 * @see ButtonToolbar
056 * @see DropdownButton
057 * @see SplitDropdownButton
058 * @see NavbarButton
059 */
060// @formatter:on
061public class Button extends IconAnchor implements HasClickHandlers,
062                HasDoubleClickHandlers, HasEnabled, HasType<ButtonType>,
063                HasAllDragAndDropHandlers, HasAllFocusHandlers, HasAllGestureHandlers,
064                HasAllKeyHandlers, HasAllMouseHandlers, HasAllTouchHandlers {
065
066        private final LoadingStateBehavior state = new LoadingStateBehavior();
067    private ButtonType type;
068    private ButtonSize size;
069
070        /**
071         * Creates an empty Button.
072         */
073        public Button() {
074                super();
075                addStyleName(Constants.BTN);
076        }
077
078        /**
079         * Creates an Button with ClickHandler
080         * @param handler Butotn ClickHandler
081         */
082        public Button(ClickHandler handler) {
083            this();
084            addClickHandler(handler);
085
086        }
087
088        /**
089         * Creates a Button with the given caption.
090         *
091         * @param caption
092         *            the caption of the Button
093         */
094        public Button(String caption) {
095                this();
096                setText(caption);
097        }
098
099        /**
100         * Create Button with click handler.
101         * @param caption the caption of the Button
102         * @param handler Button Click Handler
103         */
104        public Button(String caption, ClickHandler handler) {
105            this(caption);
106            addClickHandler(handler);
107        }
108
109        /**
110         * Creates a Button with the given caption and icon.
111         *
112         * @param caption
113         *            the caption of the Button
114         * @param icon
115         *            the Icon on the caption's left
116         */
117        public Button(String caption, IconType icon) {
118                this(caption);
119                setIcon(icon);
120        }
121
122    /**
123     * Creates a Button with the given caption and icon and ClickHandler.
124     *
125     * @param caption
126     *            the caption of the Button
127     * @param icon
128     *            the Icon on the caption's left
129     * @param handler
130     *            the ClickHandler of the Button.
131     */
132        public Button(String caption, IconType icon, ClickHandler handler) {
133            this(caption, icon);
134            addClickHandler(handler);
135        }
136
137        /**
138         * Sets the type of the Button.
139         * <p>
140         * Different types give the button a different look.
141         *
142         * @param type
143         *            the type of the Button.
144         */
145        public void setType(ButtonType type) {
146                this.type = type;
147        StyleHelper.changeStyle(this, type, ButtonType.class);
148        }
149
150        /**
151         * Sets the size of the Button.
152         *
153         * @param size
154         *            the size of the Button.
155         */
156        public void setSize(ButtonSize size) {
157                this.size = size;
158        StyleHelper.changeStyle(this, size, ButtonSize.class);
159        }
160
161        /**
162         * Whether the Button is enabled or disabled.
163         * <p>
164         * A disabled widget cannot be used.
165         *
166         * @return <code>false</code> if the Button is disabled.
167         */
168        public boolean isEnabled() {
169                return !getStyleName().contains(Constants.DISABLED);
170        }
171
172        /**
173         * Sets whether the Button is enabled or disabled.
174         * <p>
175         * A disabled widget cannot be used.
176         *
177         * @param enabled
178         *            <code>false</code> if the Button should be disabled. Default:
179         *            <code>true</code>
180         */
181        public void setEnabled(boolean enabled) {
182                if (enabled)
183                        removeStyleName(Constants.DISABLED);
184                else
185                        addStyleName(Constants.DISABLED);
186        }
187
188        /**
189         * Enable ou disable the data-toggle behavior.
190         *
191         * @param toggle
192         *            <code>true</code> will enable this behavior.
193         *            <code>false</code> will disable it or do nothing if it never
194         *            was enabled.
195         */
196        public void setToggle(boolean toggle) {
197                if (toggle)
198                        getElement().setAttribute(Constants.DATA_TOGGLE, "button");
199                else
200                        getElement().removeAttribute(Constants.DATA_TOGGLE);
201        }
202
203    /**
204     * Verify if the property "toggle" is set.
205     *
206     * @return  true: if the data-toggle is equal 'button'
207     *          false: otherwise
208     */
209    public boolean isToggle() {
210        return getElement().getAttribute(Constants.DATA_TOGGLE).equals("button");
211    }
212
213    /**
214     * Verify if the button is toggled.
215     * @return true: if yes (it will contain the "active" styleclass
216     *          false: otherwise.
217     */
218    public boolean isToggled() {
219        return getStyleName().toLowerCase().contains("active");
220    }
221
222        /**
223         * Set a Loading Text to show when some action are in work with this button.
224         *
225         * @see LoadingStateBehavior
226         * @param text
227         */
228        public void setLoadingText(String text) {
229                if (text == null || text.trim().isEmpty()) {
230                        getElement().removeAttribute(Constants.DATA_LOADING_TEXT);
231                        return;
232                }
233
234                getElement().setAttribute(Constants.DATA_LOADING_TEXT, text);
235        }
236
237        /**
238         * Set a Loading Text to show when some action are completed with this
239         * button.
240         *
241         * @see LoadingStateBehavior
242         * @param text
243         */
244        public void setCompleteText(String text) {
245                if (text == null || text.trim().isEmpty()) {
246                        getElement().removeAttribute(Constants.DATA_COMPLETE_TEXT);
247                        return;
248                }
249
250                getElement().setAttribute(Constants.DATA_COMPLETE_TEXT, text);
251        }
252
253        /**
254         * A simple DSL to change the button state to loading, complete, or reset
255         * it.
256         *
257         * @return
258         */
259        public LoadingStateBehavior state() {
260                return state;
261        }
262
263        /**
264         * A simple class to encapsulate the button state managing from the user.
265         *
266         * @author Carlos Alexandro Becker
267         */
268        public class LoadingStateBehavior {
269
270                /**
271                 * put the button in the loading state.
272                 */
273                public void loading() {
274                        setLoadingBehavior("loading");
275                }
276
277                /**
278                 * reset the button state.
279                 */
280                public void reset() {
281                        setLoadingBehavior("reset");
282                }
283
284                /**
285                 * put the button in the completed state.
286                 */
287                public void complete() {
288                        setLoadingBehavior("complete");
289                }
290
291        private void setLoadingBehavior(String behavior) {
292            // Remove icon because it will be removed by Bootstrap's "$(element).button(behavior)" anyway
293            icon.removeFromParent();
294
295            setLoadingBehavior(getElement(), behavior);
296
297            // Recreate icon and add it to inner content with setText()
298            icon = new Icon(icon.getIconType());
299            setText(getText());
300        }
301
302                private native void setLoadingBehavior(Element e, String behavior) /*-{
303                        $wnd.jQuery(e).button(behavior);
304                }-*/;
305        }
306
307        /**
308         * {@inheritDoc}
309         */
310        public HandlerRegistration addClickHandler(ClickHandler handler) {
311                return addDomHandler(handler, ClickEvent.getType());
312        }
313
314        /**
315         * {@inheritDoc}
316         */
317        public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
318                return addDomHandler(handler, DoubleClickEvent.getType());
319        }
320
321        /**
322         * {@inheritDoc}
323         */
324        public HandlerRegistration addDragEndHandler(DragEndHandler handler) {
325                return addDomHandler(handler, DragEndEvent.getType());
326        }
327
328        /**
329         * {@inheritDoc}
330         */
331        public HandlerRegistration addDragEnterHandler(DragEnterHandler handler) {
332                return addDomHandler(handler, DragEnterEvent.getType());
333        }
334
335        /**
336         * {@inheritDoc}
337         */
338        public HandlerRegistration addDragLeaveHandler(DragLeaveHandler handler) {
339                return addDomHandler(handler, DragLeaveEvent.getType());
340        }
341
342        /**
343         * {@inheritDoc}
344         */
345        public HandlerRegistration addDragHandler(DragHandler handler) {
346                return addDomHandler(handler, DragEvent.getType());
347        }
348
349        /**
350         * {@inheritDoc}
351         */
352        public HandlerRegistration addDragOverHandler(DragOverHandler handler) {
353                return addDomHandler(handler, DragOverEvent.getType());
354        }
355
356        /**
357         * {@inheritDoc}
358         */
359        public HandlerRegistration addDragStartHandler(DragStartHandler handler) {
360                return addDomHandler(handler, DragStartEvent.getType());
361        }
362
363        /**
364         * {@inheritDoc}
365         */
366        public HandlerRegistration addDropHandler(DropHandler handler) {
367                return addDomHandler(handler, DropEvent.getType());
368        }
369
370        /**
371         * {@inheritDoc}
372         */
373        public HandlerRegistration addFocusHandler(FocusHandler handler) {
374                return addDomHandler(handler, FocusEvent.getType());
375        }
376
377        /**
378         * {@inheritDoc}
379         */
380        public HandlerRegistration addBlurHandler(BlurHandler handler) {
381                return addDomHandler(handler, BlurEvent.getType());
382        }
383
384        /**
385         * {@inheritDoc}
386         */
387        public HandlerRegistration addGestureStartHandler(
388                        GestureStartHandler handler) {
389                return addDomHandler(handler, GestureStartEvent.getType());
390        }
391
392        /**
393         * {@inheritDoc}
394         */
395        public HandlerRegistration addGestureChangeHandler(
396                        GestureChangeHandler handler) {
397                return addDomHandler(handler, GestureChangeEvent.getType());
398        }
399
400        /**
401         * {@inheritDoc}
402         */
403        public HandlerRegistration addGestureEndHandler(GestureEndHandler handler) {
404                return addDomHandler(handler, GestureEndEvent.getType());
405        }
406
407        /**
408         * {@inheritDoc}
409         */
410        public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
411                return addDomHandler(handler, KeyUpEvent.getType());
412        }
413
414        /**
415         * {@inheritDoc}
416         */
417        public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
418                return addDomHandler(handler, KeyDownEvent.getType());
419        }
420
421        /**
422         * {@inheritDoc}
423         */
424        public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
425                return addDomHandler(handler, KeyPressEvent.getType());
426        }
427
428        /**
429         * {@inheritDoc}
430         */
431        public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
432                return addDomHandler(handler, MouseDownEvent.getType());
433        }
434
435        /**
436         * {@inheritDoc}
437         */
438        public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
439                return addDomHandler(handler, MouseUpEvent.getType());
440        }
441
442        /**
443         * {@inheritDoc}
444         */
445        public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
446                return addDomHandler(handler, MouseOutEvent.getType());
447        }
448
449        /**
450         * {@inheritDoc}
451         */
452        public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
453                return addDomHandler(handler, MouseOverEvent.getType());
454        }
455
456        /**
457         * {@inheritDoc}
458         */
459        public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
460                return addDomHandler(handler, MouseMoveEvent.getType());
461        }
462
463        /**
464         * {@inheritDoc}
465         */
466        public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler) {
467                return addDomHandler(handler, MouseWheelEvent.getType());
468        }
469
470        /**
471         * {@inheritDoc}
472         */
473        public HandlerRegistration addTouchStartHandler(TouchStartHandler handler) {
474                return addDomHandler(handler, TouchStartEvent.getType());
475        }
476
477        /**
478         * {@inheritDoc}
479         */
480        public HandlerRegistration addTouchMoveHandler(TouchMoveHandler handler) {
481                return addDomHandler(handler, TouchMoveEvent.getType());
482        }
483
484        /**
485         * {@inheritDoc}
486         */
487        public HandlerRegistration addTouchEndHandler(TouchEndHandler handler) {
488                return addDomHandler(handler, TouchEndEvent.getType());
489        }
490
491        /**
492         * {@inheritDoc}
493         */
494        public HandlerRegistration addTouchCancelHandler(TouchCancelHandler handler) {
495                return addDomHandler(handler, TouchCancelEvent.getType());
496        }
497
498    /**
499     * Get Button Type
500     * @return Current Button Type
501     */
502    public ButtonType getType() {
503        return type;
504    }
505
506    /**
507     * Get Button Size
508     * @return Current Button Size
509     */
510    public ButtonSize getSize() {
511        return size;
512    }
513    
514    /**
515     * Set element as a Block Level Button
516     * @param block true:Block Level false:Default
517     */
518    public void setBlock(boolean block) {
519        setStyleName(Constants.BTN_BLOCK, block);
520    }
521
522    /**
523     * Programmatic equivalent of the user clicking the button.
524     */
525    public void click() {
526        NativeEvent event = Document.get().createClickEvent(0, 0, 0, 0, 0, false, false, false, false);
527        DomEvent.fireNativeEvent(event, this);
528    }
529
530    /**
531     * Buttons can act as a dismiss button when inside a {@link Modal} or {@link Alert}.
532     * <p/>
533     * UiBinder example:
534     * <code><pre>
535     * &lt;b:Modal&gt;
536     *     &lt;b:ModalFooter&gt;
537     *         &lt;b:Button text="Close" dismiss="MODAL"/&gt;
538     *     &lt;/b:ModalFooter&gt;
539     * &lt;/b:Modal&gt;
540     * </pre></code>
541     * <p/>
542     * See <a href="http://twitter.github.io/bootstrap/javascript.html#modals">Bootstrap</a> documentation.
543     *
544     * @param type Type of dismiss ("modal" or "alert")
545     * @since 2.3.2.0
546     */
547    public void setDismiss(final DismissType type) {
548        getElement().setAttribute(Constants.DATA_DISMISS, type.get());
549    }
550}