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.DivWidget;
022import com.github.gwtbootstrap.client.ui.resources.Bootstrap;
023import com.google.gwt.core.client.GWT;
024import com.google.gwt.core.client.Scheduler;
025import com.google.gwt.core.client.Scheduler.ScheduledCommand;
026import com.google.gwt.dom.client.Element;
027import com.google.gwt.dom.client.NativeEvent;
028import com.google.gwt.event.dom.client.DomEvent;
029import com.google.gwt.event.shared.EventHandler;
030import com.google.gwt.event.shared.HandlerRegistration;
031import com.google.gwt.user.client.Event;
032import com.google.gwt.user.client.ui.IsWidget;
033import com.google.gwt.user.client.ui.Widget;
034
035//@formatter:off
036/**
037 * The container for a tabbable nav.
038 * 
039 * @since 2.0.4.0
040 * @author Dominik Mayer
041 * @author ohashi keisuke
042 */
043//@formatter:on
044public class TabPanel extends DivWidget {
045
046    public static class ShowEvent extends DomEvent<ShowEvent.Handler> {
047        private static final Type<ShowEvent.Handler> TYPE = new Type<ShowEvent.Handler>(
048                "show", new ShowEvent());
049
050        private TabLink target;
051        private TabLink relatedTarget;
052
053        protected ShowEvent() {
054        }
055
056        public ShowEvent(NativeEvent event) {
057            setNativeEvent(event);
058            if(Element.is(event.getRelatedEventTarget())) {
059                setRelativeElement(Element.as(event.getRelatedEventTarget()));
060            }
061        }
062
063        public interface Handler extends EventHandler {
064
065            void onShow(ShowEvent showEvent);
066        }
067
068        @Override
069        protected void dispatch(Handler handler) {
070            handler.onShow(this);
071        }
072
073        @Override
074        public com.google.gwt.event.dom.client.DomEvent.Type<ShowEvent.Handler> getAssociatedType() {
075            return TYPE;
076        }
077
078        /**
079         * Get target
080         * 
081         * @return target
082         */
083        public TabLink getTarget() {
084            return target;
085        }
086
087        /**
088         * Set target
089         * 
090         * @param target
091         *            target
092         */
093        public void setTarget(TabLink target) {
094            this.target = target;
095        }
096
097        /**
098         * Get relatedTarget。
099         * 
100         * @return relatedTarget
101         */
102        public TabLink getRelatedTarget() {
103            return relatedTarget;
104        }
105
106        /**
107         * Set relatedTarget
108         * 
109         * @param relatedTarget
110         *            relatedTarget
111         */
112        public void setRelatedTarget(TabLink relatedTarget) {
113            this.relatedTarget = relatedTarget;
114        }
115    }
116
117    public static class ShownEvent extends DomEvent<ShownEvent.Handler> {
118        private static final Type<ShownEvent.Handler> TYPE = new Type<ShownEvent.Handler>(
119                "shown", new ShownEvent());
120
121        private TabLink target;
122        private TabLink relatedTarget;
123
124        protected ShownEvent() {
125        }
126
127        public ShownEvent(NativeEvent event) {
128            setNativeEvent(event);
129            if(Element.is(event.getRelatedEventTarget())) {
130                setRelativeElement(Element.as(event.getRelatedEventTarget()));
131            }
132        }
133
134        public interface Handler extends EventHandler {
135
136            void onShow(ShownEvent shownEvent);
137        }
138
139        @Override
140        protected void dispatch(Handler handler) {
141            handler.onShow(this);
142        }
143
144        @Override
145        public com.google.gwt.event.dom.client.DomEvent.Type<ShownEvent.Handler> getAssociatedType() {
146            return TYPE;
147        }
148
149        /**
150         * Get target
151         * 
152         * @return target
153         */
154        public TabLink getTarget() {
155            return target;
156        }
157
158        /**
159         * Set target
160         * 
161         * @param target
162         *            target
163         */
164        public void setTarget(TabLink target) {
165            this.target = target;
166        }
167
168        /**
169         * Get relatedTarget。
170         * 
171         * @return relatedTarget
172         */
173        public TabLink getRelatedTarget() {
174            return relatedTarget;
175        }
176
177        /**
178         * Set relatedTarget
179         * 
180         * @param relatedTarget
181         *            relatedTarget
182         */
183        public void setRelatedTarget(TabLink relatedTarget) {
184            this.relatedTarget = relatedTarget;
185        }
186    }
187
188    private static class TabContent extends DivWidget {
189
190        public TabContent() {
191            setStyleName(Bootstrap.tab_content);
192        }
193    }
194
195    private NavTabs tabs = new NavTabs();
196
197    private List<TabLink> tabLinkList = new ArrayList<TabLink>();
198
199    private TabContent tabContent = new TabContent();
200
201    /**
202     * Create an empty {@link Bootstrap.Tabs#ABOVE} style TabPanel.
203     */
204    public TabPanel() {
205        this(Bootstrap.Tabs.ABOVE);
206    }
207
208    /**
209     * Create an empty TabPanel.
210     * @param position tab position.
211     */
212    public TabPanel(Bootstrap.Tabs position) {
213        setStyle(position);
214        if(Bootstrap.Tabs.BELOW == position) {
215            //tabs should be added after content to display it below content in this case
216            super.add(tabContent);
217            super.add(tabs);
218        } else {
219            super.add(tabs);
220            super.add(tabContent);
221        }
222
223        setHandlerFunctions(getElement());
224    }
225
226    /**
227     * Set tab position
228     * @param position tab position.
229     */
230    public void setTabPosition(String position) {
231        if (tabs.getParent() != null) {
232            remove(tabs);
233            remove(tabContent);
234        }
235
236        if (position.equalsIgnoreCase("below")) {
237            //tabs should be added after content to display it below content in this case
238            setStyle(Bootstrap.Tabs.BELOW);
239            super.add(tabContent);
240            super.add(tabs);
241        } else if (position.equalsIgnoreCase("left")) {
242            setStyle(Bootstrap.Tabs.LEFT);
243            super.add(tabs);
244            super.add(tabContent);
245        } else if (position.equalsIgnoreCase("right")) {
246            setStyle(Bootstrap.Tabs.RIGHT);
247            super.add(tabs);
248            super.add(tabContent);
249        } else {
250            setStyle(Bootstrap.Tabs.ABOVE);
251            super.add(tabs);
252            super.add(tabContent);
253        }
254    }
255
256    @Override
257    public void add(Widget child) {
258        if (child instanceof TabPane) {
259            add((TabPane) child);
260            return;
261        }
262
263        if (child instanceof TabLink) {
264            add((TabLink) child);
265            return;
266        }
267
268        if (child instanceof DropdownTab) {
269            add((DropdownTab) child);
270            return;
271        }
272
273        if (GWT.isProdMode()) {
274            throw new IllegalArgumentException(
275                    "TabPanel can add only TabPane or TabLink or Tab or DorpdownTab. you added "
276                            + child);
277        }
278    }
279
280    private void add(DropdownTab dropdownTab) {
281        tabs.add(dropdownTab);
282
283        List<Tab> tabList = dropdownTab.getTabList();
284        for (Tab tab : tabList) {
285            tabLinkList.add(tab.asTabLink());
286            TabPane tabPane = tab.getTabPane();
287            tabContent.add(tabPane);
288        }
289    }
290
291    private void add(TabPane child) {
292        if (child.isCreateTabLink()) {
293            TabLink tabLink = new TabLink(child);
294            tabs.add(tabLink);
295            tabLinkList.add(tabLink);
296        }
297        tabContent.add(child);
298    }
299
300    private void add(final TabLink child) {
301        if (child.isCreateTabPane() && child.getTabPane() == null) {
302            TabPane pane = new TabPane(child.getText());
303            child.setTabPane(pane);
304            tabContent.add(pane);
305        } else if (child.getTabPane() != null) {
306            tabContent.add(child.getTabPane());
307        }
308        tabs.add(child);
309        tabLinkList.add(child);
310    }
311
312    @Override
313    public void clear() {
314        tabContent.clear();
315        tabs.clear();
316        tabLinkList.clear();
317    }
318
319    /**
320     * Remove tab or tabpane.
321     * <p>
322     * If Tablink has TabPane,romve TabPane with TabLink. </pre> {@inheritDoc}
323     */
324    @Override
325    public boolean remove(int index) {
326        Widget widget = tabs.getWidget(index);
327
328        if (widget instanceof TabLink) {
329            TabLink link = (TabLink) widget;
330            if (link.getTabPane() != null) {
331                link.getTabPane().removeFromParent();
332            }
333            tabLinkList.remove(link);
334            return tabs.remove(index);
335        } else if (widget instanceof DropdownTab) {
336
337            DropdownTab dropdownTab = (DropdownTab) widget;
338
339            List<Tab> tabList = dropdownTab.getTabList();
340
341            for (Tab tab : tabList) {
342                tabLinkList.remove(tab.asTabLink());
343                if (tab.getTabPane() != null) {
344                    tab.getTabPane().removeFromParent();
345                }
346            }
347            return tabs.remove(dropdownTab);
348        } else if (widget instanceof TabPane) {
349
350            return tabContent.remove(widget);
351        }
352
353        return super.remove(widget);
354    }
355
356    /**
357     * remove TabLink or TabPane.
358     * <p>
359     * </p>
360     * {@inheritDoc}
361     */
362    @Override
363    public boolean remove(Widget w) {
364
365        if (w instanceof TabLink) {
366            TabLink link = (TabLink) w;
367            tabLinkList.remove(link);
368            if (link.getTabPane() != null) {
369                link.getTabPane().removeFromParent();
370            }
371            return tabs.remove(w);
372        } else if (w instanceof DropdownTab) {
373            DropdownTab dropdownTab = (DropdownTab) w;
374
375            List<Tab> tabList = dropdownTab.getTabList();
376
377            for (Tab tab : tabList) {
378
379                tabLinkList.remove(tab.asTabLink());
380                if (tab.getTabPane() != null) {
381                    tab.getTabPane().removeFromParent();
382                }
383            }
384
385            return tabs.remove(dropdownTab);
386
387        } else if (w instanceof TabPane) {
388            return tabContent.remove(w);
389        }
390
391        return super.remove(w);
392    }
393
394    /**
395     * {@inheritDoc}
396     */
397    @Override
398    public boolean remove(IsWidget child) {
399
400        if (child instanceof Tab) {
401            Tab tab = (Tab) child;
402
403            TabLink link = tab.asTabLink();
404
405            if (link.getTabPane() != null) {
406                link.getTabPane().removeFromParent();
407            }
408            tabLinkList.remove(link);
409            return tabs.remove(link);
410        } else if (child instanceof DropdownTab) {
411            DropdownTab tab = (DropdownTab) child;
412
413            List<Tab> tabList = tab.getTabList();
414
415            for (Tab tab2 : tabList) {
416                tabLinkList.remove(tab2.asTabLink());
417                if (tab2.getTabPane() != null) {
418                    tabContent.remove(tab2.getTabPane());
419                }
420            }
421            return super.remove(child);
422        }
423
424        return super.remove(child);
425    }
426
427    /**
428     * Activate tab by index.
429     * @param index tab index.
430     */
431    public void selectTab(int index) {
432        tabLinkList.get(index).show();
433    }
434
435    /**
436     * Get Current selected tab index.
437     * <p>
438     * if not found, return <code>-1.</code>
439     * </p>
440     * 
441     * @return tab index.
442     */
443    public int getSelectedTab() {
444
445        for (int i = 0; i < tabLinkList.size(); i++) {
446            if (tabLinkList.get(i).isActive()) {
447                return i;
448            }
449        }
450        return -1;
451    }
452
453    private void setHandlerFunctions(final TabLink e) {
454        if (isOrWasAttached()) {
455            setHandlerFunctions(e.getAnchor().getElement());
456            return;
457        }
458
459        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
460
461            @Override
462            public void execute() {
463                setHandlerFunctions(e.getAnchor().getElement());
464            }
465        });
466
467    }
468
469    protected void onShow(Event e, Element target, Element relatedTarget) {
470        ShowEvent event = new ShowEvent(e);
471        event.setTarget(findTabLink(target));
472        event.setRelatedTarget(findTabLink(relatedTarget));
473        fireEvent(event);
474    }
475
476    protected void onShown(Event e, Element target, Element relatedTarget) {
477        ShownEvent event = new ShownEvent(e);
478        event.setTarget(findTabLink(target));
479        event.setRelatedTarget(findTabLink(relatedTarget));
480        fireEvent(event);
481    }
482
483    public HandlerRegistration addShowHandler(ShowEvent.Handler handler) {
484        return addHandler(handler, ShowEvent.TYPE);
485    }
486
487    public HandlerRegistration addShownHandler(ShownEvent.Handler handler) {
488        return addHandler(handler, ShownEvent.TYPE);
489    }
490
491    private TabLink findTabLink(Element e) {
492        for (TabLink tabLink : tabLinkList)
493            if (tabLink.getAnchor().getElement().equals(e))
494                return tabLink;
495
496        return null;
497    }
498
499    //@formatter:off
500    private native void setHandlerFunctions(Element e) /*-{
501        var that = this;
502        var $this = $wnd.jQuery(e);
503    
504        $this.off('show');
505        $this.off('shown');
506    
507        $this.on('show', function(e) {
508            that.@com.github.gwtbootstrap.client.ui.TabPanel::onShow(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/dom/client/Element;)(e, e.target, e.relatedTarget);
509        });
510        $this.on('shown', function(e) {
511            that.@com.github.gwtbootstrap.client.ui.TabPanel::onShown(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/dom/client/Element;)(e, e.target, e.relatedTarget);
512        });
513    }-*/;
514    //@formatter:on
515
516}