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 java.util.ArrayList;
019import java.util.List;
020
021import com.github.gwtbootstrap.client.ui.base.ComplexWidget;
022import com.github.gwtbootstrap.client.ui.base.ListItem;
023import com.github.gwtbootstrap.client.ui.base.UnorderedList;
024import com.github.gwtbootstrap.client.ui.constants.Constants;
025import com.google.gwt.dom.client.Document;
026import com.google.gwt.user.client.ui.HasText;
027import com.google.gwt.user.client.ui.Widget;
028import com.google.gwt.user.client.ui.WidgetCollection;
029
030/**
031 * The Breadcrumbs container
032 * 
033 * <p>
034 * <h3>UiBinder Usage:</h3>
035 * <p/>
036 * <pre>
037 * {@code
038 * <b:Breadcrumbs>
039 *  <b:NavLink>Home</b:NavLink>
040 *  <b:NavLink>Accoount</b:NavLink>
041 * </b:Breadcrumbs>
042 * <b:Breadcrumbs divider=">">
043 *  <b:NavLink>Home</b:NavLink>
044 *  <b:NavLink>Accoount</b:NavLink>
045 * </b:Breadcrumbs>
046 * }
047 * 
048 * @since 2.0.4.0
049 * @author Dominik Mayer
050 * @author ohashi keisuke
051 */
052public class Breadcrumbs extends UnorderedList {
053
054    private WidgetCollection children = new WidgetCollection(this);
055    
056    private List<Divider> dividerList = new ArrayList<Divider>();
057    
058    private String divider = "/";
059    
060    private static class Divider extends ComplexWidget {
061
062        public Divider(String divider) {
063            super("span");
064            setStyleName(Constants.DIVIDER);
065            setDivider(divider);
066        }
067        
068        public void setDivider(String divider) {
069            getElement().setInnerText(divider);
070        }
071    }
072    
073    
074    /**
075     * Create a empty Breadcrumbs
076     */
077    public Breadcrumbs() {
078        setStyleName(Constants.BREADCRUMB);
079    }
080    
081    /**
082     * Create Breadcrumbs with widgets
083     * @param widgets widgets
084     */
085    public Breadcrumbs(Widget ... widgets) {
086        this();
087        
088        for (Widget widget : widgets) {
089            this.add(widget);
090        }
091    }
092    
093    /**
094     * Create Breadcrumbs with setting divider character
095     * @param divider separate char (ex : ">")
096     */
097    public Breadcrumbs(String divider) {
098        this();
099        this.setDivider(divider);
100    }
101    
102    /**
103     * Set separater character
104     * @param divider separater character
105     */
106    public void setDivider(String divider) {
107        if(divider == null || divider.isEmpty()) {
108            this.divider = "/";
109        } else {
110            this.divider = divider;
111        }
112        
113        for (Divider dividerWidget : dividerList) {
114            dividerWidget.setDivider(this.divider);
115        }
116        
117    }
118    
119    /**
120     * {@inheritDoc}
121     */
122    @Override
123    protected void onAttach() {
124        
125        if(!isOrWasAttached() && children.size() > 0) {
126            
127            Widget lastWidget = children.get(children.size() -1);
128            
129            for (Widget w : children) {
130                ListItem item = lastWidget.equals(w)
131                                ? change2TextListItem(w)
132                                : getOrCreateListItem(w);
133                super.add(item);
134            }
135        }
136        
137        super.onAttach();
138    }
139    
140    /**
141     * {@inheritDoc}
142     */
143    @Override
144    public void add(Widget w) {
145        
146        w.removeStyleName(Constants.ACTIVE);
147        
148        if(!isOrWasAttached()) {
149            
150            children.add(w);
151            
152            return;
153        }
154        
155        if(children.size() > 0) {
156            //Change last widget 2 Link
157            
158            //pygical remove
159            super.remove(getWidget(getWidgetCount() -1));
160            
161            ListItem item = getOrCreateListItem(children.get(children.size() -1));
162            
163            super.add(item);
164        }
165        
166        ListItem newest = change2TextListItem(w);
167        
168        super.add(newest);
169        
170        children.add(w);
171    }
172
173    private ListItem getOrCreateListItem(Widget lastWidget) {
174        ListItem item = null;
175
176        Divider dividerWidget = new Divider(divider);
177        if(lastWidget instanceof NavWidget) {
178            NavWidget w = (NavWidget)lastWidget;
179            
180            if(hasDivier(w)) {
181                
182                return w;
183            } else {
184                dividerList.add(dividerWidget);
185                w.addWidget(dividerWidget);
186                return w;
187            }
188        } else if(lastWidget instanceof ListItem) {
189            item = (ListItem)lastWidget;
190        } else {
191            item = new ListItem(lastWidget);
192        }
193        
194        if(hasDivier(item)) {
195            return item;
196        }
197        
198        item.add(dividerWidget);
199        dividerList.add(dividerWidget);
200        
201        return item;
202    }
203
204    private boolean hasDivier(ListItem item) {
205        
206        for(Widget w : item) {
207            if(w instanceof Divider) {
208                return true;
209            }
210        }
211        return false;
212    }
213
214    private ListItem change2TextListItem(Widget w) {
215        String text = null;
216        
217        if (w instanceof HasText) {
218            text = ((HasText) w).getText();
219        } else {
220            text = w.getElement().getInnerText();
221        }
222
223        ListItem newest = new ListItem();
224        newest.setStyleName(Constants.ACTIVE);
225        newest.getElement().appendChild(Document.get().createTextNode(text));
226        return newest;
227    }
228    
229    /**
230     * {@inheritDoc}
231     */
232    @Override
233    public boolean remove(Widget w) {
234        
235        if(!isOrWasAttached() && children.contains(w)) {
236            children.remove(w);
237            return true;
238        }
239
240        if(getWidgetIndex(w) < 0 && !children.contains(w)) {
241            return false;
242        }
243        
244        boolean isLastWidget = (children.indexOf(w) == children.size() -1) || (getWidgetIndex(w) == getWidgetCount() -1);
245        
246        if(getWidgetIndex(w) >= 0 && children.contains(w)) {
247            children.remove(w);
248            super.remove(w);
249        } else if(getWidgetIndex(w) >= 0 && !children.contains(w)) {
250            children.remove(getWidgetIndex(w));
251            super.remove(w);
252        } else if(getWidgetIndex(w) < 0 && children.contains(w)) {
253            return remove(getWidget(children.indexOf(w)));
254        } else {
255            return false;
256        }
257        
258        if(isLastWidget && getWidgetCount() > 0) {
259            Widget l = getWidget(getWidgetCount() -1);
260            super.remove(l);
261            super.add(change2TextListItem(l));
262        }
263        
264        return true;
265    }
266    
267    /**
268     * {@inheritDoc}
269     */
270    @Override
271    public void clear() {
272        super.clear();
273        children = new WidgetCollection(this);
274        dividerList.clear();
275    }
276}