Java tutorial
/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package mat.client.shared.ui; import mat.client.shared.MatContext; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.HasAllKeyHandlers; import com.google.gwt.event.dom.client.HasClickHandlers; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.logical.shared.BeforeSelectionEvent; import com.google.gwt.event.logical.shared.BeforeSelectionHandler; import com.google.gwt.event.logical.shared.HasBeforeSelectionHandlers; import com.google.gwt.event.logical.shared.HasSelectionHandlers; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Accessibility; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HasWordWrap; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.KeyboardListener; import com.google.gwt.user.client.ui.KeyboardListenerCollection; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.SourcesClickEvents; import com.google.gwt.user.client.ui.SourcesTabEvents; import com.google.gwt.user.client.ui.TabBar; import com.google.gwt.user.client.ui.TabListener; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.Widget; /** * A horizontal bar of folder-style tabs, most commonly used as part of a. * * {@link com.google.gwt.user.client.ui.TabPanel}. * <p> * <img class='gallery' src='doc-files/TabBar.png'/> * </p> * <h3>CSS Style Rules</h3> * <ul class='css'> * <li>.gwt-TabBar { the tab bar itself }</li> * <li>.gwt-TabBar .gwt-TabBarFirst { the left edge of the bar }</li> * <li>.gwt-TabBar .gwt-TabBarFirst-wrapper { table cell around the left edge }</li> * <li>.gwt-TabBar .gwt-TabBarRest { the right edge of the bar }</li> * <li>.gwt-TabBar .gwt-TabBarRest-wrapper { table cell around the right edge }</li> * <li>.gwt-TabBar .gwt-TabBarItem { unselected tabs }</li> * <li>.gwt-TabBar .gwt-TabBarItem-wrapper { table cell around tab }</li> * <li>.gwt-TabBar .gwt-TabBarItem-selected { additional style for selected * <p> * <h3>Example</h3> * {@example com.google.gwt.examples.TabBarExample} * </p> */ /** * Almost an exact implementation of the GWT TabBar class * see method onBrowserEvent in inner class: ClickDelegatePanel * Our MAT need was to hijack the tab selection (mouse click or key) * and output an alert if a check fails * revision by aschmidt * */ @SuppressWarnings("deprecation") public class MATTabBar extends Composite implements SourcesTabEvents, HasBeforeSelectionHandlers<Integer>, HasSelectionHandlers<Integer>, ClickListener, KeyboardListener { /** * Set of characteristic interfaces supported by {@link TabBar} tabs. * * Note that this set might expand over time, so implement this interface at * your own risk. */ public interface Tab extends HasAllKeyHandlers, HasClickHandlers, HasWordWrap { /** * Check if the underlying widget implements {@link HasWordWrap}. * * @return true if the widget implements {@link HasWordWrap} */ boolean hasWordWrap(); } /** * <code>ClickDelegatePanel</code> decorates any widget with the minimal * amount of machinery to receive clicks for delegation to the parent. * {@link SourcesClickEvents} is not implemented due to the fact that only a * single observer is needed. */ private class ClickDelegatePanel extends Composite implements Tab { /** The focusable panel. */ private FocusPanel focusablePanel; /** The enabled. */ private boolean enabled = true; /** * Instantiates a new click delegate panel. * * @param child * the child */ ClickDelegatePanel(Widget child) { focusablePanel = new FocusPanel(child); SimplePanel wrapperWidget = createTabTextWrapper(); if (wrapperWidget == null) { initWidget(focusablePanel); } else { wrapperWidget.setWidget(focusablePanel); initWidget(wrapperWidget); } sinkEvents(Event.ONCLICK | Event.ONKEYDOWN); } /* (non-Javadoc) * @see com.google.gwt.event.dom.client.HasClickHandlers#addClickHandler(com.google.gwt.event.dom.client.ClickHandler) */ public HandlerRegistration addClickHandler(ClickHandler handler) { return addHandler(handler, ClickEvent.getType()); } /* (non-Javadoc) * @see com.google.gwt.event.dom.client.HasKeyDownHandlers#addKeyDownHandler(com.google.gwt.event.dom.client.KeyDownHandler) */ public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { return addHandler(handler, KeyDownEvent.getType()); } /* (non-Javadoc) * @see com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler(com.google.gwt.event.dom.client.KeyPressHandler) */ public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) { return addDomHandler(handler, KeyPressEvent.getType()); } /* (non-Javadoc) * @see com.google.gwt.event.dom.client.HasKeyUpHandlers#addKeyUpHandler(com.google.gwt.event.dom.client.KeyUpHandler) */ public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) { return addDomHandler(handler, KeyUpEvent.getType()); } /** * Gets the focusable panel. * * @return the focusable panel */ public SimplePanel getFocusablePanel() { return focusablePanel; } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.HasWordWrap#getWordWrap() */ public boolean getWordWrap() { if (hasWordWrap()) { return ((HasWordWrap) focusablePanel.getWidget()).getWordWrap(); } throw new UnsupportedOperationException("Widget does not implement HasWordWrap"); } /* (non-Javadoc) * @see mat.client.shared.ui.MATTabBar.Tab#hasWordWrap() */ public boolean hasWordWrap() { return focusablePanel.getWidget() instanceof HasWordWrap; } /** * Checks if is enabled. * * @return true, if is enabled */ public boolean isEnabled() { return enabled; } /** * NOTE pass on browser event only if the loading check passes else show an * alert message. * * @param event * the event */ @Override public void onBrowserEvent(Event event) { if (!enabled) { return; } if (!MatContext.get().isLoading()) { // No need for call to super. switch (DOM.eventGetType(event)) { case Event.ONCLICK: selectTabByTabWidget(this); onClick(this); break; case Event.ONKEYDOWN: if (((char) DOM.eventGetKeyCode(event)) == KeyCodes.KEY_ENTER) { selectTabByTabWidget(this); } onKeyDown(this, (char) event.getKeyCode(), KeyboardListenerCollection.getKeyboardModifiers(event)); break; } super.onBrowserEvent(event); } else { MatContext.get().fireLoadingAlert(); } } /** * Sets the enabled. * * @param enabled * the new enabled */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.HasWordWrap#setWordWrap(boolean) */ public void setWordWrap(boolean wrap) { if (hasWordWrap()) { ((HasWordWrap) focusablePanel.getWidget()).setWordWrap(wrap); } else { throw new UnsupportedOperationException("Widget does not implement HasWordWrap"); } } } /** The Constant STYLENAME_DEFAULT. */ private static final String STYLENAME_DEFAULT = "gwt-TabBarItem"; /** The panel. */ private HorizontalPanel panel = new HorizontalPanel(); /** The selected tab. */ private Widget selectedTab; /** * Creates an empty tab bar. */ public MATTabBar() { panel.getElement().setId("panel_HorizontalPanel"); initWidget(panel); sinkEvents(Event.ONCLICK); setStyleName("gwt-TabBar"); // Add a11y role "tablist" Accessibility.setRole(panel.getElement(), Accessibility.ROLE_TABLIST); panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_BOTTOM); HTML first = new HTML(" ", true), rest = new HTML(" ", true); first.setStyleName("gwt-TabBarFirst"); rest.setStyleName("gwt-TabBarRest"); first.setHeight("100%"); rest.setHeight("100%"); panel.add(first); panel.add(rest); first.setHeight("100%"); panel.setCellHeight(first, "100%"); panel.setCellWidth(rest, "100%"); setStyleName(first.getElement().getParentElement(), "gwt-TabBarFirst-wrapper"); setStyleName(rest.getElement().getParentElement(), "gwt-TabBarRest-wrapper"); } /* (non-Javadoc) * @see com.google.gwt.event.logical.shared.HasBeforeSelectionHandlers#addBeforeSelectionHandler(com.google.gwt.event.logical.shared.BeforeSelectionHandler) */ public HandlerRegistration addBeforeSelectionHandler(BeforeSelectionHandler<Integer> handler) { return addHandler(handler, BeforeSelectionEvent.getType()); } /* (non-Javadoc) * @see com.google.gwt.event.logical.shared.HasSelectionHandlers#addSelectionHandler(com.google.gwt.event.logical.shared.SelectionHandler) */ public HandlerRegistration addSelectionHandler(SelectionHandler<Integer> handler) { return addHandler(handler, SelectionEvent.getType()); } /** * Adds a new tab with the specified text. * * @param text the new tab's text */ public void addTab(String text) { insertTab(text, getTabCount()); } /** * Adds a new tab with the specified text. * * @param text the new tab's text * @param asHTML <code>true</code> to treat the specified text as html */ public void addTab(String text, boolean asHTML) { insertTab(text, asHTML, getTabCount()); } /** * Adds a new tab with the specified widget. * * @param widget the new tab's widget */ public void addTab(Widget widget) { insertTab(widget, getTabCount()); } /** * Adds the tab listener. * * @param listener * the listener * @deprecated Use {@link #addBeforeSelectionHandler} and {#link * #addSelectionHandler} instead */ @Deprecated public void addTabListener(TabListener listener) { MATListenerWrapper.WrappedTabListener.add(this, listener); } /** * Gets the tab that is currently selected. * * @return the selected tab */ public int getSelectedTab() { if (selectedTab == null) { return -1; } return panel.getWidgetIndex(selectedTab) - 1; } /** * Gets the given tab. * * This method is final because the Tab interface will expand. Therefore * it is highly likely that subclasses which implemented this method would end up * breaking. * * @param index the tab's index * @return the tab wrapper */ public final Tab getTab(int index) { if (index >= getTabCount()) { return null; } ClickDelegatePanel p = (ClickDelegatePanel) panel.getWidget(index + 1); return p; } /** * Gets the number of tabs present. * * @return the tab count */ public int getTabCount() { return panel.getWidgetCount() - 2; } /** * Gets the specified tab's HTML. * * @param index the index of the tab whose HTML is to be retrieved * @return the tab's HTML */ public String getTabHTML(int index) { if (index >= getTabCount()) { return null; } ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1); SimplePanel focusablePanel = delPanel.getFocusablePanel(); Widget widget = focusablePanel.getWidget(); if (widget instanceof HTML) { return ((HTML) widget).getHTML(); } else if (widget instanceof Label) { return ((Label) widget).getText(); } else { // This will be a focusable panel holding a user-supplied widget. return focusablePanel.getElement().getParentElement().getInnerHTML(); } } /** * Inserts a new tab at the specified index. * * @param text the new tab's text * @param asHTML <code>true</code> to treat the specified text as HTML * @param beforeIndex the index before which this tab will be inserted */ public void insertTab(String text, boolean asHTML, int beforeIndex) { checkInsertBeforeTabIndex(beforeIndex); Label item; if (asHTML) { item = new HTML(text); } else { item = new Label(text); } item.setWordWrap(false); insertTabWidget(item, beforeIndex); } /** * Inserts a new tab at the specified index. * * @param text the new tab's text * @param beforeIndex the index before which this tab will be inserted */ public void insertTab(String text, int beforeIndex) { insertTab(text, false, beforeIndex); } /** * Inserts a new tab at the specified index. * * @param widget widget to be used in the new tab * @param beforeIndex the index before which this tab will be inserted */ public void insertTab(Widget widget, int beforeIndex) { insertTabWidget(widget, beforeIndex); } /** * Check if a tab is enabled or disabled. If disabled, the user cannot select * the tab. * * @param index the index of the tab * @return true if the tab is enabled, false if disabled */ public boolean isTabEnabled(int index) { assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds"; ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1); return delPanel.isEnabled(); } /** * On click. * * @param sender * the sender * @deprecated add a {@link BeforeSelectionHandler} instead. Alternatively, * if you need to access to the individual tabs, add a click * handler to each {@link Tab} element instead. */ @Deprecated public void onClick(Widget sender) { } /** * On key down. * * @param sender * the sender * @param keyCode * the key code * @param modifiers * the modifiers * @deprecated add a key down handler to the individual {@link Tab} objects * instead. */ @Deprecated public void onKeyDown(Widget sender, char keyCode, int modifiers) { } /** * On key press. * * @param sender * the sender * @param keyCode * the key code * @param modifiers * the modifiers * @deprecated this method has been doing nothing for the entire last * release, if what you wanted to do was to listen to key press * events on tabs, add the key press handler to the individual * tab wrappers instead. */ @Deprecated public void onKeyPress(Widget sender, char keyCode, int modifiers) { } /** * On key up. * * @param sender * the sender * @param keyCode * the key code * @param modifiers * the modifiers * @deprecated this method has been doing nothing for the entire last * release, if what you wanted to do was to listen to key up * events on tabs, add the key up handler to the individual tab * wrappers instead. */ @Deprecated public void onKeyUp(Widget sender, char keyCode, int modifiers) { } /** * Removes the tab at the specified index. * * @param index the index of the tab to be removed */ public void removeTab(int index) { checkTabIndex(index); // (index + 1) to account for 'first' placeholder widget. Widget toRemove = panel.getWidget(index + 1); if (toRemove == selectedTab) { selectedTab = null; } panel.remove(toRemove); } /** * Removes the tab listener. * * @param listener * the listener * @deprecated Instead use the {@link HandlerRegistration#removeHandler} * call on the object returned by an add*Handler method */ @Deprecated public void removeTabListener(TabListener listener) { MATListenerWrapper.WrappedTabListener.remove(this, listener); } /** * Programmatically selects the specified tab. Use index -1 to specify that no * tab should be selected. * * @param index the index of the tab to be selected * @return <code>true</code> if successful, <code>false</code> if the change * is denied by the {@link BeforeSelectionHandler}. */ public boolean selectTab(int index) { checkTabIndex(index); BeforeSelectionEvent<?> event = BeforeSelectionEvent.fire(this, index); if (event != null && event.isCanceled()) { return false; } // Check for -1. setSelectionStyle(selectedTab, false); if (index == -1) { selectedTab = null; return true; } if (!MatContext.get().isErrorTab()) {// if there is any error to be thrown, do not fire Event. selectedTab = panel.getWidget(index + 1); setSelectionStyle(selectedTab, true); SelectionEvent.fire(this, index); } else { selectedTab = panel.getWidget(MatContext.get().getErrorTabIndex() + 1); setSelectionStyle(selectedTab, true); MatContext.get().setErrorTab(false); } return true; } /** * Enable or disable a tab. When disabled, users cannot select the tab. * * @param index the index of the tab to enable or disable * @param enabled true to enable, false to disable */ public void setTabEnabled(int index, boolean enabled) { assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds"; // Style the wrapper ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1); delPanel.setEnabled(enabled); setStyleName(delPanel.getElement(), "gwt-TabBarItem-disabled", !enabled); setStyleName(delPanel.getElement().getParentElement(), "gwt-TabBarItem-wrapper-disabled", !enabled); } /** * Sets a tab's contents via HTML. * * Use care when setting an object's HTML; it is an easy way to expose * script-based security problems. Consider using * * @param index * the index of the tab whose HTML is to be set * @param html * the tab new HTML {@link #setTabText(int, String)} whenever * possible. */ public void setTabHTML(int index, String html) { assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds"; ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1); SimplePanel focusablePanel = delPanel.getFocusablePanel(); focusablePanel.setWidget(new HTML(html, false)); } /** * Sets a tab's text contents. * * @param index the index of the tab whose text is to be set * @param text the object's new text */ public void setTabText(int index, String text) { assert (index >= 0) && (index < getTabCount()) : "Tab index out of bounds"; ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(index + 1); SimplePanel focusablePanel = delPanel.getFocusablePanel(); // It is not safe to check if the current widget is an instanceof Label and // reuse it here because HTML is an instanceof Label. Leaving an HTML would // throw off the results of getTabHTML(int). focusablePanel.setWidget(new Label(text, false)); } /** * Create a {@link SimplePanel} that will wrap the contents in a tab. * Subclasses can use this method to wrap tabs in decorator panels. * * @return a {@link SimplePanel} to wrap the tab contents, or null to leave * tabs unwrapped */ protected SimplePanel createTabTextWrapper() { return null; } /** * Inserts a new tab at the specified index. * * @param widget widget to be used in the new tab * @param beforeIndex the index before which this tab will be inserted */ protected void insertTabWidget(Widget widget, int beforeIndex) { checkInsertBeforeTabIndex(beforeIndex); ClickDelegatePanel delWidget = new ClickDelegatePanel(widget); delWidget.setStyleName(STYLENAME_DEFAULT); // Add a11y role "tab" SimplePanel focusablePanel = delWidget.getFocusablePanel(); Accessibility.setRole(focusablePanel.getElement(), Accessibility.ROLE_TAB); panel.insert(delWidget, beforeIndex + 1); setStyleName(DOM.getParent(delWidget.getElement()), STYLENAME_DEFAULT + "-wrapper", true); } /** * <b>Affected Elements:</b> * <ul> * <li>-tab# = The element containing the contents of the tab.</li> * <li>-tab-wrapper# = The cell containing the tab at the index.</li> * </ul> * * @param baseID * the base id * @see UIObject#onEnsureDebugId(String) */ @Override protected void onEnsureDebugId(String baseID) { super.onEnsureDebugId(baseID); int numTabs = getTabCount(); for (int i = 0; i < numTabs; i++) { ClickDelegatePanel delPanel = (ClickDelegatePanel) panel.getWidget(i + 1); SimplePanel focusablePanel = delPanel.getFocusablePanel(); ensureDebugId(focusablePanel.getElement(), baseID, "tab" + i); ensureDebugId(DOM.getParent(delPanel.getElement()), baseID, "tab-wrapper" + i); } } /** * Check insert before tab index. * * @param beforeIndex * the before index */ private void checkInsertBeforeTabIndex(int beforeIndex) { if ((beforeIndex < 0) || (beforeIndex > getTabCount())) { throw new IndexOutOfBoundsException(); } } /** * Check tab index. * * @param index * the index */ private void checkTabIndex(int index) { if ((index < -1) || (index >= getTabCount())) { throw new IndexOutOfBoundsException(); } } /** * Selects the tab corresponding to the widget for the tab. To be clear the * widget for the tab is not the widget INSIDE of the tab; it is the widget * used to represent the tab itself. * * @param tabWidget The widget for the tab to be selected * @return true if the tab corresponding to the widget for the tab could * located and selected, false otherwise */ private boolean selectTabByTabWidget(Widget tabWidget) { int numTabs = panel.getWidgetCount() - 1; for (int i = 1; i < numTabs; ++i) { if (panel.getWidget(i) == tabWidget) { return selectTab(i - 1); } } return false; } /** * Sets the selection style. * * @param item * the item * @param selected * the selected */ private void setSelectionStyle(Widget item, boolean selected) { if (item != null) { if (selected) { item.addStyleName("gwt-TabBarItem-selected"); setStyleName(DOM.getParent(item.getElement()), "gwt-TabBarItem-wrapper-selected", true); } else { item.removeStyleName("gwt-TabBarItem-selected"); setStyleName(DOM.getParent(item.getElement()), "gwt-TabBarItem-wrapper-selected", false); } } } }