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