001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.gwt.client.ui;
029
030import org.opencms.gwt.client.I_CmsDescendantResizeHandler;
031import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle;
032import org.opencms.gwt.client.util.CmsDomUtil;
033import org.opencms.util.CmsStringUtil;
034
035import java.util.HashMap;
036import java.util.Iterator;
037import java.util.List;
038import java.util.Map;
039import java.util.Map.Entry;
040
041import com.google.gwt.core.client.Scheduler;
042import com.google.gwt.core.client.Scheduler.ScheduledCommand;
043import com.google.gwt.dom.client.Element;
044import com.google.gwt.dom.client.Style.Overflow;
045import com.google.gwt.dom.client.Style.Unit;
046import com.google.gwt.event.logical.shared.AttachEvent;
047import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
048import com.google.gwt.event.logical.shared.SelectionHandler;
049import com.google.gwt.event.shared.HandlerRegistration;
050import com.google.gwt.user.client.ui.Composite;
051import com.google.gwt.user.client.ui.DeckLayoutPanel;
052import com.google.gwt.user.client.ui.FlowPanel;
053import com.google.gwt.user.client.ui.LayoutPanel;
054import com.google.gwt.user.client.ui.TabLayoutPanel;
055import com.google.gwt.user.client.ui.Widget;
056
057/**
058 * Wrapper class for @see com.google.user.client.ui.TabLayoutPanel.<p>
059 *
060 * Layout class for a panel with several tabs. The tabbed panel should be set inside a widget with given width and height.
061 * For table based layouts the height of the parent cell should be set explicitly.
062 *
063 * As layout options two height for the tabbar are provided: 32px("standard") and 25px("small").
064 *
065 * @param <E> the tab widget type
066 *
067 * @since 8.0.0
068 *
069 */
070public class CmsTabbedPanel<E extends Widget> extends Composite implements I_CmsDescendantResizeHandler, Iterable<E> {
071
072    /** Enumeration with layout keys. */
073    public enum CmsTabbedPanelStyle {
074
075        /** Button style. */
076        buttonTabs(30, I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().buttonTabs(),
077        I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().black()),
078
079        /** Classic style. */
080        classicTabs(28, I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().classicTabs(), null);
081
082        /** The default tabbar height. */
083        public static final CmsTabbedPanelStyle DEFAULT = buttonTabs;
084
085        /** Property name. */
086        private int m_barHeight;
087
088        /** The general panel style name. */
089        private String m_styleClass;
090
091        /** The tab color style name. */
092        private String m_tabColorClass;
093
094        /**
095         * Constructor.<p>
096         *
097         * @param barHeight the height of the bar
098         * @param styleClass the tab style class
099         * @param tabColorClass the tab color
100         */
101        private CmsTabbedPanelStyle(int barHeight, String styleClass, String tabColorClass) {
102
103            m_barHeight = barHeight;
104            m_styleClass = styleClass;
105            m_tabColorClass = tabColorClass;
106        }
107
108        /**
109         * Returns the barHeight.<p>
110         *
111         * @return the barHeight
112         */
113        public int getBarHeight() {
114
115            return m_barHeight;
116        }
117
118        /**
119         * Returns the styleClass.<p>
120         *
121         * @return the styleClass
122         */
123        public String getStyleClass() {
124
125            return m_styleClass;
126        }
127
128        /**
129         * Returns the tabColorClass.<p>
130         *
131         * @return the tabColorClass
132         */
133        public String getTabColorClass() {
134
135            return m_tabColorClass;
136        }
137    }
138
139    /**
140     * Extending the TabLayoutPanel class to allow height adjustments to the tab bar.<p>
141     */
142    protected class TabPanel extends TabLayoutPanel {
143
144        /** The tab content panel. */
145        private DeckLayoutPanel m_contentPanel;
146
147        /** The tab bar. */
148        private FlowPanel m_tabBar;
149
150        /**
151         * Constructor.<p>
152         *
153         * @param barHeight the tab bar height
154         * @param barUnit the height unit
155         */
156        public TabPanel(double barHeight, Unit barUnit) {
157
158            super(barHeight, barUnit);
159            LayoutPanel tabLayout = (LayoutPanel)getWidget();
160            // Find the tab bar, which is the first flow panel in the LayoutPanel
161            for (int i = 0; i < tabLayout.getWidgetCount(); ++i) {
162                Widget widget = tabLayout.getWidget(i);
163                if (widget instanceof FlowPanel) {
164                    m_tabBar = (FlowPanel)widget;
165                    break; // tab bar found
166                }
167            }
168
169            for (int i = 0; i < tabLayout.getWidgetCount(); ++i) {
170                Widget widget = tabLayout.getWidget(i);
171                if (widget instanceof DeckLayoutPanel) {
172                    m_contentPanel = (DeckLayoutPanel)widget;
173                    break; // tab bar found
174                }
175            }
176        }
177
178        /**
179         * Checks the tab bar for necessary height adjustments.<p>
180         */
181        protected void checkTabOverflow() {
182
183            int height = m_tabBar.getOffsetHeight();
184            m_contentPanel.getElement().getParentElement().getStyle().setTop(height, Unit.PX);
185        }
186    }
187
188    /** The TabLayoutPanel widget. */
189    TabPanel m_tabPanel;
190
191    /** Auto resize mode. */
192    private boolean m_autoResize;
193
194    /** Offset which is added to the measured tab content height to resize the panel. */
195    private int m_autoResizeHeightDelta;
196
197    /** Stores the indexes and the title of disabled tabs. */
198    private Map<Integer, String> m_disabledTabIndexes = new HashMap<Integer, String>();
199
200    /** The tab panel style. */
201    private CmsTabbedPanelStyle m_panelStyle;
202
203    /** A map from ids to tabs. */
204    private Map<String, E> m_tabsById = new HashMap<String, E>();
205
206    /**
207     * The default constructor for an empty tabbed panel. <p>
208     */
209    public CmsTabbedPanel() {
210
211        this(CmsTabbedPanelStyle.DEFAULT);
212    }
213
214    /**
215     * The constructor for an empty tabbed panel. <p>
216     *
217     * @param tabbedPanelStyle the pre-defined height of the tabbar, can be "small" or "standard"
218     */
219    public CmsTabbedPanel(CmsTabbedPanelStyle tabbedPanelStyle) {
220
221        m_tabPanel = new TabPanel(tabbedPanelStyle.getBarHeight(), Unit.PX);
222        m_panelStyle = tabbedPanelStyle;
223
224        // All composites must call initWidget() in their constructors.
225        initWidget(m_tabPanel);
226
227        Element tabRootEl = m_tabPanel.getElement();
228        // set an additional css class for the parent element of the .gwt-TabLayoutPanelTabs element
229        List<Element> tabBarDivs = CmsDomUtil.getElementsByClass(
230            I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cmsTabLayoutPanelTabs(),
231            CmsDomUtil.Tag.div,
232            tabRootEl);
233        if (tabBarDivs.size() == 1) {
234            tabBarDivs.get(0).getParentElement().setClassName(
235                I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cmsTabLayoutPanelTabBar()
236                    + " "
237                    + I_CmsLayoutBundle.INSTANCE.generalCss().cornerTop());
238            if (m_panelStyle.getTabColorClass() != null) {
239                tabBarDivs.get(0).getParentElement().addClassName(m_panelStyle.getTabColorClass());
240            }
241        }
242
243        m_tabPanel.setStyleName(m_panelStyle.getStyleClass());
244        m_tabPanel.addStyleName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cmsTabLayoutPanel());
245        m_tabPanel.addStyleName(
246            I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll()
247                + " "
248                + I_CmsLayoutBundle.INSTANCE.generalCss().textMedium());
249
250        m_tabPanel.addAttachHandler(new AttachEvent.Handler() {
251
252            /**
253             * @see com.google.gwt.event.logical.shared.AttachEvent.Handler#onAttachOrDetach(com.google.gwt.event.logical.shared.AttachEvent)
254             */
255            public void onAttachOrDetach(AttachEvent event) {
256
257                setOverflowVisibleToContent();
258                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
259
260                    public void execute() {
261
262                        m_tabPanel.checkTabOverflow();
263                    }
264                });
265            }
266        });
267    }
268
269    /**
270     * Add a new tab with the provided name and content.<p>
271     *
272     * Wrapper function for {@link com.google.gwt.user.client.ui.TabLayoutPanel#add(Widget, String)}
273     *
274     * @param tabContent the widget to add as a tab
275     * @param tabName the name of the tab to display in the tabbar
276     */
277    public void add(E tabContent, String tabName) {
278
279        tabContent.addStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll());
280        m_tabPanel.add(tabContent, CmsDomUtil.stripHtml(tabName));
281
282        Element tabRootEl = m_tabPanel.getElement();
283        // set an additional css class for the parent element of the .gwt-TabLayoutPanelTabs element
284        List<Element> tabDivs = CmsDomUtil.getElementsByClass(
285            I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cmsTabLayoutPanelTab(),
286            CmsDomUtil.Tag.div,
287            tabRootEl);
288
289        Iterator<Element> it = tabDivs.iterator();
290        boolean first = true;
291        while (it.hasNext()) {
292
293            Element e = it.next();
294            e.removeClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cornerLeft());
295            e.removeClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cornerRight());
296            if (first) {
297                e.addClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cornerLeft());
298                first = false;
299            }
300            if (!it.hasNext()) {
301                e.addClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cornerRight());
302            }
303        }
304        m_tabPanel.checkTabOverflow();
305    }
306
307    /**
308     * Add the before selection handler to the tabbed panel.<p>
309     *
310     * Wrapper function for {@link com.google.gwt.user.client.ui.TabLayoutPanel#addBeforeSelectionHandler(BeforeSelectionHandler)}
311     *
312     * @param handler the before selection handler
313     * @return the registration for the event
314     */
315    public HandlerRegistration addBeforeSelectionHandler(BeforeSelectionHandler<Integer> handler) {
316
317        return m_tabPanel.addBeforeSelectionHandler(handler);
318    }
319
320    /**
321     * Adds a tab with a user-defined id.<p>
322     *
323     * @param tabContent the tab content
324     * @param tabName the tab name
325     * @param tabId the tab id
326     */
327    public void addNamed(E tabContent, String tabName, String tabId) {
328
329        add(tabContent, tabName);
330        m_tabsById.put(tabId, tabContent);
331    }
332
333    /**
334     * Adds a SelectionEvent handler to the tabbed panel.<p>
335     *
336     * Wrapper function for {@link com.google.gwt.user.client.ui.TabLayoutPanel#addSelectionHandler(SelectionHandler)}
337     *
338     * @param handler the selection handler
339     * @return the registration for the event
340     */
341    public HandlerRegistration addSelectionHandler(SelectionHandler<Integer> handler) {
342
343        return m_tabPanel.addSelectionHandler(handler);
344    }
345
346    /**
347     * Add a new tab with the provided name and content and additional left margin.<p>
348     *
349     * @param tabContent the widget to add as a tab
350     * @param tabName the name of the tab to display in the tabbar
351     */
352    public void addWithLeftMargin(E tabContent, String tabName) {
353
354        tabContent.addStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll());
355        m_tabPanel.add(tabContent, CmsDomUtil.stripHtml(tabName));
356
357        int tabIndex = m_tabPanel.getWidgetIndex(tabContent);
358        Element tabElement = getTabElement(tabIndex);
359        if (tabElement != null) {
360            tabElement.addClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().tabLeftMargin());
361            if (!m_panelStyle.equals(CmsTabbedPanelStyle.classicTabs)) {
362                tabElement.addClassName(I_CmsLayoutBundle.INSTANCE.generalCss().buttonCornerAll());
363                tabElement.addClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().borderAll());
364            }
365        }
366        m_tabPanel.checkTabOverflow();
367    }
368
369    /**
370     * Disables the tab with the given index.<p>
371     *
372     * @param tabContent the content of the tab that should be disabled
373     * @param reason the reason why the tab is disabled
374     */
375    public void disableTab(E tabContent, String reason) {
376
377        Integer index = Integer.valueOf(m_tabPanel.getWidgetIndex(tabContent));
378        Element tab = getTabElement(index.intValue());
379        if ((tab != null) && !m_disabledTabIndexes.containsKey(index)) {
380            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(tab.getTitle())) {
381                m_disabledTabIndexes.put(index, tab.getTitle());
382            } else {
383                m_disabledTabIndexes.put(index, "");
384            }
385            tab.addClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().tabDisabled());
386            tab.setTitle(reason);
387        }
388    }
389
390    /**
391     * Enables the tab with the given index.<p>
392     *
393     * @param tabContent the content of the tab that should be enabled
394     */
395    public void enableTab(E tabContent) {
396
397        Integer index = Integer.valueOf(m_tabPanel.getWidgetIndex(tabContent));
398        Element tab = getTabElement(index.intValue());
399        if ((tab != null) && m_disabledTabIndexes.containsKey(index)) {
400            tab.removeClassName(I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().tabDisabled());
401            tab.setTitle(m_disabledTabIndexes.get(index));
402            m_disabledTabIndexes.remove(index);
403        }
404    }
405
406    /**
407     * Returns the id of the selected tab.<p>
408     *
409     * @return the selected tab id
410     */
411    public String getSelectedId() {
412
413        Widget tab = getWidget(m_tabPanel.getSelectedIndex());
414        String tabId = null;
415        for (Entry<String, E> tabEntry : m_tabsById.entrySet()) {
416            if (tabEntry.getValue().equals(tab)) {
417                tabId = tabEntry.getKey();
418                break;
419            }
420        }
421        return tabId;
422    }
423
424    /**
425     * Gets the index of the currently-selected tab.<p>
426     *
427     * Wrapper function for {@link com.google.gwt.user.client.ui.TabLayoutPanel#getSelectedIndex()}
428     *
429     * @return the selected index, or -1 if none is selected.
430     */
431    public int getSelectedIndex() {
432
433        return m_tabPanel.getSelectedIndex();
434
435    }
436
437    /**
438     * Measures the height of the tab bar.<p>
439     *
440     * @return the tab bar height
441     */
442    public int getTabBarHeight() {
443
444        @SuppressWarnings("synthetic-access")
445        int result = m_tabPanel.m_tabBar.getOffsetHeight();
446        return result;
447
448    }
449
450    /**
451     * Finds a tab with a given id.<p>
452     *
453     * @param tabId a tab id
454     *
455     * @return the tab with the given id
456     */
457    public E getTabById(String tabId) {
458
459        return m_tabsById.get(tabId);
460    }
461
462    /**
463     * Gets the number of child widgets in this panel.<p>
464     *
465     * Wrapper function for {@link com.google.gwt.user.client.ui.TabLayoutPanel#getWidgetCount()}
466     *
467     * @return the number of children
468     */
469    public int getTabCount() {
470
471        return m_tabPanel.getWidgetCount();
472    }
473
474    /**
475     * Returns the index of the tab to the given child element.<p>
476     *
477     * @param child the tab child
478     *
479     * @return the tab index
480     */
481    public int getTabIndex(Element child) {
482
483        int index = 0;
484        for (Widget tab : m_tabPanel) {
485            if (tab.getElement().isOrHasChild(child)) {
486                return index;
487            }
488            index++;
489        }
490        return -1;
491    }
492
493    /**
494     * Returns the tab text for a given tab.<p>
495     *
496     * @param pos the index of the tab
497     *
498     * @return the text of the tab
499     */
500    public String getTabText(int pos) {
501
502        return m_tabPanel.getTabWidget(pos).getElement().getInnerText();
503    }
504
505    /**
506     * Returns the tab widget.<p>
507     * This will not be the tab content but the tab itself.<p>
508     *
509     * @param index the tab index
510     *
511     * @return the tab widget
512     */
513    public Widget getTabWidget(int index) {
514
515        return m_tabPanel.getTabWidget(index);
516    }
517
518    /**
519     * Gets the child widget at the specified index.<p>
520     *
521     * Wrapper function for {@link com.google.gwt.user.client.ui.TabLayoutPanel#getWidget(int)}
522     *
523     * @param tabIndex the child widget's index
524     * @return the child widget
525     */
526    @SuppressWarnings("unchecked")
527    public E getWidget(int tabIndex) {
528
529        return (E)m_tabPanel.getWidget(tabIndex);
530    }
531
532    /**
533     * Inserts a widget into the panel. If the Widget is already attached, it will be moved to the requested index.<p>
534     *
535     * Wrapper function for {@link com.google.gwt.user.client.ui.TabLayoutPanel#insert(Widget, String, int)}
536     *
537     * @param tabContent the widget to be added
538     * @param tabName the text to be shown on its tab
539     * @param beforeIndex  the index before which it will be inserted
540     */
541    public void insert(E tabContent, String tabName, int beforeIndex) {
542
543        m_tabPanel.insert(tabContent, tabName, beforeIndex);
544        m_tabPanel.checkTabOverflow();
545    }
546
547    /**
548     * Returns <code>true</code> if the tab with the given index is disabled, <code>false</code> otherwise.<p>
549     *
550     * @param tabIndex the tab index
551     *
552     * @return <code>true</code> if the tab with the given index is disabled, <code>false</code> otherwise
553     */
554    public boolean isDisabledTab(int tabIndex) {
555
556        return m_disabledTabIndexes.containsKey(Integer.valueOf(tabIndex));
557    }
558
559    /**
560     * Returns an iterator over all tabs.<p>
561     *
562     * @return the iterator
563     */
564    @SuppressWarnings("unchecked")
565    public Iterator<E> iterator() {
566
567        return (Iterator<E>)m_tabPanel.iterator();
568    }
569
570    /**
571     * @see org.opencms.gwt.client.I_CmsDescendantResizeHandler#onResizeDescendant()
572     */
573    public void onResizeDescendant() {
574
575        if (m_autoResize) {
576            Widget w = m_tabPanel.getWidget(getSelectedIndex());
577            if (w instanceof CmsTabContentWrapper) {
578                w = ((CmsTabContentWrapper)w).getWidget();
579            }
580            int h = w.getOffsetHeight() + m_autoResizeHeightDelta;
581            setHeight(h + "px");
582        }
583    }
584
585    /**
586     * Removes the tab with the given index.<p>
587     *
588     * @param tabIndex the index of the tab which should be removed
589     */
590    public void removeTab(int tabIndex) {
591
592        m_tabPanel.remove(tabIndex);
593        m_tabPanel.checkTabOverflow();
594    }
595
596    /**
597     * Delegate method.<p>
598     *
599     * @see com.google.gwt.user.client.ui.TabLayoutPanel#selectTab(Widget index)
600     *
601     * @param tabWidget the tab widget to select
602     */
603    public void selectTab(E tabWidget) {
604
605        m_tabPanel.selectTab(tabWidget);
606    }
607
608    /**
609     * Delegate method.<p>
610     *
611     * @see com.google.gwt.user.client.ui.TabLayoutPanel#selectTab(Widget index)
612     *
613     * @param tabWidget the tab widget to select
614     * @param fireEvent <code>true</code> to fire the tab event
615     */
616    public void selectTab(E tabWidget, boolean fireEvent) {
617
618        m_tabPanel.selectTab(tabWidget, fireEvent);
619    }
620
621    /**
622     * Delegate method.<p>
623     *
624     * @see com.google.gwt.user.client.ui.TabLayoutPanel#selectTab(int index)
625     *
626     * @param tabIndex the index of the tab to be selected
627     */
628    public void selectTab(int tabIndex) {
629
630        m_tabPanel.selectTab(tabIndex);
631    }
632
633    /**
634     * Delegate method.<p>
635     *
636     * @see com.google.gwt.user.client.ui.TabLayoutPanel#selectTab(int index)
637     *
638     * @param tabIndex the index of the tab to be selected
639     * @param fireEvent <code>true</code> to fire the tab event
640     */
641    public void selectTab(int tabIndex, boolean fireEvent) {
642
643        m_tabPanel.selectTab(tabIndex, fireEvent);
644    }
645
646    /**
647     * Enables or disables auto-resizing.<p>
648     *
649     * @param autoResize the auto resize flag value
650     */
651    public void setAutoResize(boolean autoResize) {
652
653        m_autoResize = autoResize;
654    }
655
656    /**
657     * Sets a value which is added to the height of a tab content to change the tabbed panel height.<p>
658     *
659     * @param heightDelta the height difference
660     */
661    public void setAutoResizeHeightDelta(int heightDelta) {
662
663        m_autoResizeHeightDelta = heightDelta;
664    }
665
666    /**
667     * Sets the text of a given tab.<p>
668     *
669     * @param pos the index of the tab
670     * @param text the new text for the tab
671     */
672    public void setTabText(int pos, String text) {
673
674        m_tabPanel.setTabText(pos, text);
675
676    }
677
678    /**
679     * Returns the tab layout panel.<p>
680     *
681     * @return the tab layout panel
682     */
683    protected TabLayoutPanel getTabPanel() {
684
685        return m_tabPanel;
686    }
687
688    /**
689     * @see com.google.gwt.user.client.ui.Widget#onLoad()
690     */
691    @Override
692    protected void onLoad() {
693
694        super.onLoad();
695        m_tabPanel.checkTabOverflow();
696        // force layout after insertion into DOM to deal with IE layout problems
697        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
698
699            public void execute() {
700
701                getTabPanel().forceLayout();
702
703            }
704        });
705
706    }
707
708    /**
709     * Sets the overflow of the tab layout content's parent to visible.<p>
710     */
711    protected void setOverflowVisibleToContent() {
712
713        Element tabRoot = m_tabPanel.getElement();
714        // set an additional css class for the parent element of the .gwt-TabLayoutPanelTabs element
715        List<Element> tabContentDivs = CmsDomUtil.getElementsByClass(
716            I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cmsTabLayoutPanelContent(),
717            CmsDomUtil.Tag.div,
718            tabRoot);
719        tabContentDivs.addAll(
720            CmsDomUtil.getElementsByClass("gwt-TabLayoutPanelContentContainer", CmsDomUtil.Tag.div, tabRoot));
721        for (Element e : tabContentDivs) {
722            e.getParentElement().getStyle().setOverflow(Overflow.VISIBLE);
723        }
724
725    }
726
727    /**
728     * Returns the tab element for the given index.<p>
729     *
730     * @param tabIndex the tab index to get the tab element for
731     *
732     * @return the tab element for the given index
733     */
734    private Element getTabElement(int tabIndex) {
735
736        Element tabRootEl = m_tabPanel.getElement();
737        // set an additional css class for the parent element of the .gwt-TabLayoutPanelTabs element
738        List<Element> tabDivs = CmsDomUtil.getElementsByClass(
739            I_CmsLayoutBundle.INSTANCE.tabbedPanelCss().cmsTabLayoutPanelTab(),
740            CmsDomUtil.Tag.div,
741            tabRootEl);
742        if ((tabDivs != null) && (tabDivs.size() > tabIndex)) {
743            return tabDivs.get(tabIndex);
744        }
745        return null;
746    }
747}