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.base;
017
018import com.github.gwtbootstrap.client.ui.Icon;
019import com.github.gwtbootstrap.client.ui.constants.Constants;
020import com.github.gwtbootstrap.client.ui.constants.IconSize;
021import com.github.gwtbootstrap.client.ui.constants.IconType;
022import com.google.gwt.dom.client.AnchorElement;
023import com.google.gwt.dom.client.Document;
024import com.google.gwt.dom.client.Text;
025import com.google.gwt.event.dom.client.ClickEvent;
026import com.google.gwt.event.dom.client.ClickHandler;
027import com.google.gwt.event.dom.client.HasClickHandlers;
028import com.google.gwt.event.shared.HandlerRegistration;
029import com.google.gwt.user.client.DOM;
030import com.google.gwt.user.client.Event;
031import com.google.gwt.user.client.ui.Focusable;
032import com.google.gwt.user.client.ui.HasEnabled;
033import com.google.gwt.user.client.ui.HasName;
034import com.google.gwt.user.client.ui.HasText;
035import com.google.gwt.user.client.ui.impl.FocusImpl;
036
037/**
038 * An Anchor with optional image and caret.
039 * 
040 * <p>
041 * It uses a HTML {@code <a>} tag and can contain text and child widgets. But
042 * not both at the same time.
043 * </p>
044 * 
045 * <p>
046 * <h3>UiBinder Usage:</h3>
047 * {@code <b:IconAnchor icon="plane" href="www.twitter.com">Some Text</b:IconAnchor>}
048 * </p>
049 * 
050 * <p>
051 * Here we add a second Icon:
052 * 
053 * <pre>
054 * {@code <b:IconAnchor icon="STAR" text="There is a widget so the text goes here">
055 *     <b:Icon type="STAR" />
056 * </b:IconAnchor>}
057 * </pre>
058 * 
059 * All parameter are optional. All setters can be used as parameters.
060 * </p>
061 * 
062 * @since 2.0.4.0
063 * 
064 * @author Dominik Mayer
065 * @author ohashi keisuke
066 */
067public class IconAnchor extends ComplexWidget implements HasText, HasIcon, HasHref, HasClickHandlers, HasEnabled, Focusable, HasName {
068
069    private static final FocusImpl impl = FocusImpl.getFocusImplForWidget();
070
071        private Icon icon = new Icon();
072
073        private Text text = Document.get().createTextNode("");
074
075        private Caret caret = new Caret();
076
077        /**
078         * Creates the widget and sets the {@code href} property to
079         * {@code javascript:;} in order to avoid problems when clicking on it.
080         */
081        public IconAnchor() {
082                super("a");
083                super.add(icon);
084                super.getElement().appendChild(text);
085                setEmptyHref();
086        }
087
088        /**
089         * {@inheritDoc}
090         */
091        public void setIcon(IconType type) {
092                if (type != null)
093                        this.icon.setType(type);
094        }
095
096        /**
097         * {@inheritDoc}
098         */
099        @Override
100        public void setIconSize(IconSize size) {
101                icon.setIconSize(size);
102        }
103
104        /**
105         * {@inheritDoc}
106         */
107        public void setText(String text) {
108            
109            this.text.setData(" " + text + " ");
110        }
111
112        /**
113         * {@inheritDoc}
114         */
115        public String getText() {
116                return text.getData();
117        }
118
119        /**
120         * {@inheritDoc}
121         */
122        public void setHref(String href) {
123                getElement().setAttribute("href", href);
124        }
125
126        /**
127         * {@inheritDoc}
128         */
129        public String getHref() {
130                return getElement().getAttribute("href");
131        }
132
133        /**
134         * Shows or hides the caret.
135         * 
136         * @param visible
137         *            <code>true</code> if the caret should be shown.
138         */
139        public void setCaret(boolean visible) {
140                if (visible)
141                        super.add(caret);
142                else
143                        super.remove(caret);
144        }
145
146        /**
147         * {@inheritDoc}
148         */
149        public void setTargetHistoryToken(String targetHistoryToken) {
150                setHref("#" + targetHistoryToken);
151        }
152
153        /**
154         * {@inheritDoc}
155         */
156        public String getTargetHistoryToken() {
157                String[] hrefs = getHref().split("#");
158                return hrefs[1];
159        }
160
161        /**
162         * Sets the <code>href</code>property of this element to "javascript:;" in
163         * order to get another cursor (hand).
164         */
165        public void setEmptyHref() {
166                setHref(Constants.EMPTY_HREF);
167        }
168
169        /**
170         * {@inheritDoc}
171         */
172        @Override
173        public HandlerRegistration addClickHandler(ClickHandler handler) {
174                return addDomHandler(handler, ClickEvent.getType());
175        }
176
177        /**
178         * {@inheritDoc}
179         */
180        @Override
181        public boolean isEnabled() {
182                return !DOM.getElementPropertyBoolean(getElement(), "disabled");
183        }
184
185        /**
186         * {@inheritDoc}
187         */
188        @Override
189        public void setEnabled(boolean enabled) {
190                DOM.setElementPropertyBoolean(getElement(), "disabled", !enabled);
191        }
192
193        /**
194         * {@inheritDoc}
195         */
196        @Override
197        public void onBrowserEvent(Event event) {
198                switch (DOM.eventGetType(event)) {
199                case Event.ONCLICK:
200                        if (isEnabled()) {
201                                super.onBrowserEvent(event);
202                        }
203                        break;
204                default:
205                        super.onBrowserEvent(event);
206                        break;
207                }
208
209        }
210
211    @Override
212    public int getTabIndex() {
213        return impl.getTabIndex(getElement());
214    }
215
216    @Override
217    public void setAccessKey(char key) {
218        DOM.setElementProperty(getElement(), "accessKey", "" + key);
219    }
220
221    @Override
222    public void setFocus(boolean focused) {
223        if (focused) {
224            impl.focus(getElement());
225        } else {
226            impl.blur(getElement());
227        }
228    }
229
230    @Override
231    public void setTabIndex(int index) {
232        impl.setTabIndex(getElement(), index);
233    }
234
235    @Override
236    protected void onAttach() {
237        super.onAttach();
238
239        // Accessibility: setting tab index to be 0 by default, ensuring element
240        // appears in tab sequence. We must ensure that the element doesn't already
241        // have a tabIndex set. This is not a problem for normal widgets, but when
242        // a widget is used to wrap an existing static element, it can already have
243        // a tabIndex.
244        int tabIndex = getTabIndex();
245        if (-1 == tabIndex) {
246            setTabIndex(0);
247        }
248    }
249    
250    /**
251     * Set active style name.
252     * @param active <code>true</code> : set active <code>false</code> : unset active
253     */
254    public void setActive(boolean active) {
255        setStyleName(Constants.ACTIVE, active);
256    }
257    
258    /**
259     * Has the active css style name?
260     * @return <code>true</code>: has <code>false</code> : none.
261     */
262    public boolean isActive() {
263        return getStyleName().contains(Constants.ACTIVE);
264    }
265
266    /**
267     * {@inheritDoc}
268     */
269    @Override
270    public void setName(String name) {
271        getAnchorElement().setName(name);
272    }
273
274    /**
275     * {@inheritDoc}
276     */
277    @Override
278    public String getName() {
279        return getAnchorElement().getName();
280    }
281    
282    /** 
283     * Set target attribute
284     * @param target target name
285     */
286    public void setTarget(String target) {
287        getAnchorElement().setTarget(target);
288    }
289    
290    /**
291     * Get target attribute value
292     * @return target attribute value
293     */
294    public String getTarget() {
295        return getAnchorElement().getTarget();
296    }
297    
298    protected AnchorElement getAnchorElement() {
299        return AnchorElement.as(getElement());
300    }
301
302    /**
303     * {@inheritDoc}
304     */
305    @Override
306    public void setCustomIconStyle(String customIconStyle) {
307        icon.addStyleName(customIconStyle);
308    }
309    
310}