aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/ui/TabSheet.java
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2012-08-13 18:34:33 +0300
committerArtur Signell <artur@vaadin.com>2012-08-13 19:18:33 +0300
commite85d933b25cc3c5cc85eb7eb4b13b950fd8e1569 (patch)
tree9ab6f13f7188cab44bbd979b1cf620f15328a03f /server/src/com/vaadin/ui/TabSheet.java
parent14dd4d0b28c76eb994b181a4570f3adec53342e6 (diff)
downloadvaadin-framework-e85d933b25cc3c5cc85eb7eb4b13b950fd8e1569.tar.gz
vaadin-framework-e85d933b25cc3c5cc85eb7eb4b13b950fd8e1569.zip
Moved server files to a server src folder (#9299)
Diffstat (limited to 'server/src/com/vaadin/ui/TabSheet.java')
-rw-r--r--server/src/com/vaadin/ui/TabSheet.java1328
1 files changed, 1328 insertions, 0 deletions
diff --git a/server/src/com/vaadin/ui/TabSheet.java b/server/src/com/vaadin/ui/TabSheet.java
new file mode 100644
index 0000000000..c52e9394c0
--- /dev/null
+++ b/server/src/com/vaadin/ui/TabSheet.java
@@ -0,0 +1,1328 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.vaadin.event.FieldEvents.BlurEvent;
+import com.vaadin.event.FieldEvents.BlurListener;
+import com.vaadin.event.FieldEvents.BlurNotifier;
+import com.vaadin.event.FieldEvents.FocusEvent;
+import com.vaadin.event.FieldEvents.FocusListener;
+import com.vaadin.event.FieldEvents.FocusNotifier;
+import com.vaadin.terminal.ErrorMessage;
+import com.vaadin.terminal.KeyMapper;
+import com.vaadin.terminal.LegacyPaint;
+import com.vaadin.terminal.PaintException;
+import com.vaadin.terminal.PaintTarget;
+import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.Vaadin6Component;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.TabsheetBaseConnector;
+import com.vaadin.terminal.gwt.client.ui.tabsheet.VTabsheet;
+import com.vaadin.ui.Component.Focusable;
+import com.vaadin.ui.themes.Reindeer;
+import com.vaadin.ui.themes.Runo;
+
+/**
+ * TabSheet component.
+ *
+ * Tabs are typically identified by the component contained on the tab (see
+ * {@link ComponentContainer}), and tab metadata (including caption, icon,
+ * visibility, enabledness, closability etc.) is kept in separate {@link Tab}
+ * instances.
+ *
+ * Tabs added with {@link #addComponent(Component)} get the caption and the icon
+ * of the component at the time when the component is created, and these are not
+ * automatically updated after tab creation.
+ *
+ * A tab sheet can have multiple tab selection listeners and one tab close
+ * handler ({@link CloseHandler}), which by default removes the tab from the
+ * TabSheet.
+ *
+ * The {@link TabSheet} can be styled with the .v-tabsheet, .v-tabsheet-tabs and
+ * .v-tabsheet-content styles. Themes may also have pre-defined variations of
+ * the tab sheet presentation, such as {@link Reindeer#TABSHEET_BORDERLESS},
+ * {@link Runo#TABSHEET_SMALL} and several other styles in {@link Reindeer}.
+ *
+ * The current implementation does not load the tabs to the UI before the first
+ * time they are shown, but this may change in future releases.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+public class TabSheet extends AbstractComponentContainer implements Focusable,
+ FocusNotifier, BlurNotifier, Vaadin6Component {
+
+ /**
+ * List of component tabs (tab contents). In addition to being on this list,
+ * there is a {@link Tab} object in tabs for each tab with meta-data about
+ * the tab.
+ */
+ private final ArrayList<Component> components = new ArrayList<Component>();
+
+ /**
+ * Map containing information related to the tabs (caption, icon etc).
+ */
+ private final HashMap<Component, Tab> tabs = new HashMap<Component, Tab>();
+
+ /**
+ * Selected tab content component.
+ */
+ private Component selected = null;
+
+ /**
+ * Mapper between server-side component instances (tab contents) and keys
+ * given to the client that identify tabs.
+ */
+ private final KeyMapper<Component> keyMapper = new KeyMapper<Component>();
+
+ /**
+ * When true, the tab selection area is not displayed to the user.
+ */
+ private boolean tabsHidden;
+
+ /**
+ * Handler to be called when a tab is closed.
+ */
+ private CloseHandler closeHandler;
+
+ private int tabIndex;
+
+ /**
+ * Constructs a new Tabsheet. Tabsheet is immediate by default, and the
+ * default close handler removes the tab being closed.
+ */
+ public TabSheet() {
+ super();
+ // expand horizontally by default
+ setWidth(100, UNITS_PERCENTAGE);
+ setImmediate(true);
+ setCloseHandler(new CloseHandler() {
+
+ @Override
+ public void onTabClose(TabSheet tabsheet, Component c) {
+ tabsheet.removeComponent(c);
+ }
+ });
+ }
+
+ /**
+ * Gets the component container iterator for going through all the
+ * components (tab contents).
+ *
+ * @return the unmodifiable Iterator of the tab content components
+ */
+
+ @Override
+ public Iterator<Component> getComponentIterator() {
+ return Collections.unmodifiableList(components).iterator();
+ }
+
+ /**
+ * Gets the number of contained components (tabs). Consistent with the
+ * iterator returned by {@link #getComponentIterator()}.
+ *
+ * @return the number of contained components
+ */
+
+ @Override
+ public int getComponentCount() {
+ return components.size();
+ }
+
+ /**
+ * Removes a component and its corresponding tab.
+ *
+ * If the tab was selected, the first eligible (visible and enabled)
+ * remaining tab is selected.
+ *
+ * @param c
+ * the component to be removed.
+ */
+
+ @Override
+ public void removeComponent(Component c) {
+ if (c != null && components.contains(c)) {
+ super.removeComponent(c);
+ keyMapper.remove(c);
+ components.remove(c);
+ tabs.remove(c);
+ if (c.equals(selected)) {
+ if (components.isEmpty()) {
+ setSelected(null);
+ } else {
+ // select the first enabled and visible tab, if any
+ updateSelection();
+ fireSelectedTabChange();
+ }
+ }
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Removes a {@link Tab} and the component associated with it, as previously
+ * added with {@link #addTab(Component)},
+ * {@link #addTab(Component, String, Resource)} or
+ * {@link #addComponent(Component)}.
+ * <p>
+ * If the tab was selected, the first eligible (visible and enabled)
+ * remaining tab is selected.
+ * </p>
+ *
+ * @see #addTab(Component)
+ * @see #addTab(Component, String, Resource)
+ * @see #addComponent(Component)
+ * @see #removeComponent(Component)
+ * @param tab
+ * the Tab to remove
+ */
+ public void removeTab(Tab tab) {
+ removeComponent(tab.getComponent());
+ }
+
+ /**
+ * Adds a new tab into TabSheet. Component caption and icon are copied to
+ * the tab metadata at creation time.
+ *
+ * @see #addTab(Component)
+ *
+ * @param c
+ * the component to be added.
+ */
+
+ @Override
+ public void addComponent(Component c) {
+ addTab(c);
+ }
+
+ /**
+ * Adds a new tab into TabSheet.
+ *
+ * The first tab added to a tab sheet is automatically selected and a tab
+ * selection event is fired.
+ *
+ * If the component is already present in the tab sheet, changes its caption
+ * and returns the corresponding (old) tab, preserving other tab metadata.
+ *
+ * @param c
+ * the component to be added onto tab - should not be null.
+ * @param caption
+ * the caption to be set for the component and used rendered in
+ * tab bar
+ * @return the created {@link Tab}
+ */
+ public Tab addTab(Component c, String caption) {
+ return addTab(c, caption, null);
+ }
+
+ /**
+ * Adds a new tab into TabSheet.
+ *
+ * The first tab added to a tab sheet is automatically selected and a tab
+ * selection event is fired.
+ *
+ * If the component is already present in the tab sheet, changes its caption
+ * and icon and returns the corresponding (old) tab, preserving other tab
+ * metadata.
+ *
+ * @param c
+ * the component to be added onto tab - should not be null.
+ * @param caption
+ * the caption to be set for the component and used rendered in
+ * tab bar
+ * @param icon
+ * the icon to be set for the component and used rendered in tab
+ * bar
+ * @return the created {@link Tab}
+ */
+ public Tab addTab(Component c, String caption, Resource icon) {
+ return addTab(c, caption, icon, components.size());
+ }
+
+ /**
+ * Adds a new tab into TabSheet.
+ *
+ * The first tab added to a tab sheet is automatically selected and a tab
+ * selection event is fired.
+ *
+ * If the component is already present in the tab sheet, changes its caption
+ * and icon and returns the corresponding (old) tab, preserving other tab
+ * metadata like the position.
+ *
+ * @param c
+ * the component to be added onto tab - should not be null.
+ * @param caption
+ * the caption to be set for the component and used rendered in
+ * tab bar
+ * @param icon
+ * the icon to be set for the component and used rendered in tab
+ * bar
+ * @param position
+ * the position at where the the tab should be added.
+ * @return the created {@link Tab}
+ */
+ public Tab addTab(Component c, String caption, Resource icon, int position) {
+ if (c == null) {
+ return null;
+ } else if (tabs.containsKey(c)) {
+ Tab tab = tabs.get(c);
+ tab.setCaption(caption);
+ tab.setIcon(icon);
+ return tab;
+ } else {
+ components.add(position, c);
+
+ Tab tab = new TabSheetTabImpl(caption, icon);
+
+ tabs.put(c, tab);
+ if (selected == null) {
+ setSelected(c);
+ fireSelectedTabChange();
+ }
+ super.addComponent(c);
+ requestRepaint();
+ return tab;
+ }
+ }
+
+ /**
+ * Adds a new tab into TabSheet. Component caption and icon are copied to
+ * the tab metadata at creation time.
+ *
+ * If the tab sheet already contains the component, its tab is returned.
+ *
+ * @param c
+ * the component to be added onto tab - should not be null.
+ * @return the created {@link Tab}
+ */
+ public Tab addTab(Component c) {
+ return addTab(c, components.size());
+ }
+
+ /**
+ * Adds a new tab into TabSheet. Component caption and icon are copied to
+ * the tab metadata at creation time.
+ *
+ * If the tab sheet already contains the component, its tab is returned.
+ *
+ * @param c
+ * the component to be added onto tab - should not be null.
+ * @param position
+ * The position where the tab should be added
+ * @return the created {@link Tab}
+ */
+ public Tab addTab(Component c, int position) {
+ if (c == null) {
+ return null;
+ } else if (tabs.containsKey(c)) {
+ return tabs.get(c);
+ } else {
+ return addTab(c, c.getCaption(), c.getIcon(), position);
+ }
+ }
+
+ /**
+ * Moves all components from another container to this container. The
+ * components are removed from the other container.
+ *
+ * If the source container is a {@link TabSheet}, component captions and
+ * icons are copied from it.
+ *
+ * @param source
+ * the container components are removed from.
+ */
+
+ @Override
+ public void moveComponentsFrom(ComponentContainer source) {
+ for (final Iterator<Component> i = source.getComponentIterator(); i
+ .hasNext();) {
+ final Component c = i.next();
+ String caption = null;
+ Resource icon = null;
+ if (TabSheet.class.isAssignableFrom(source.getClass())) {
+ caption = ((TabSheet) source).getTabCaption(c);
+ icon = ((TabSheet) source).getTabIcon(c);
+ }
+ source.removeComponent(c);
+ addTab(c, caption, icon);
+
+ }
+ }
+
+ /**
+ * Paints the content of this component.
+ *
+ * @param target
+ * the paint target
+ * @throws PaintException
+ * if the paint operation failed.
+ */
+
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+
+ if (areTabsHidden()) {
+ target.addAttribute("hidetabs", true);
+ }
+
+ if (tabIndex != 0) {
+ target.addAttribute("tabindex", tabIndex);
+ }
+
+ target.startTag("tabs");
+
+ for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) {
+ final Component component = i.next();
+
+ Tab tab = tabs.get(component);
+
+ target.startTag("tab");
+ if (!tab.isEnabled() && tab.isVisible()) {
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_DISABLED, true);
+ }
+
+ if (!tab.isVisible()) {
+ target.addAttribute("hidden", true);
+ }
+
+ if (tab.isClosable()) {
+ target.addAttribute("closable", true);
+ }
+
+ // tab icon, caption and description, but used via
+ // VCaption.updateCaption(uidl)
+ final Resource icon = tab.getIcon();
+ if (icon != null) {
+ target.addAttribute(TabsheetBaseConnector.ATTRIBUTE_TAB_ICON,
+ icon);
+ }
+ final String caption = tab.getCaption();
+ if (caption != null && caption.length() > 0) {
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_CAPTION, caption);
+ }
+ ErrorMessage tabError = tab.getComponentError();
+ if (tabError != null) {
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_ERROR_MESSAGE,
+ tabError.getFormattedHtmlMessage());
+ }
+ final String description = tab.getDescription();
+ if (description != null) {
+ target.addAttribute(
+ TabsheetBaseConnector.ATTRIBUTE_TAB_DESCRIPTION,
+ description);
+ }
+
+ final String styleName = tab.getStyleName();
+ if (styleName != null && styleName.length() != 0) {
+ target.addAttribute(VTabsheet.TAB_STYLE_NAME, styleName);
+ }
+
+ target.addAttribute("key", keyMapper.key(component));
+ if (component.equals(selected)) {
+ target.addAttribute("selected", true);
+ LegacyPaint.paint(component, target);
+ }
+ target.endTag("tab");
+ }
+
+ target.endTag("tabs");
+
+ if (selected != null) {
+ target.addVariable(this, "selected", keyMapper.key(selected));
+ }
+
+ }
+
+ /**
+ * Are the tab selection parts ("tabs") hidden.
+ *
+ * @return true if the tabs are hidden in the UI
+ */
+ public boolean areTabsHidden() {
+ return tabsHidden;
+ }
+
+ /**
+ * Hides or shows the tab selection parts ("tabs").
+ *
+ * @param tabsHidden
+ * true if the tabs should be hidden
+ */
+ public void hideTabs(boolean tabsHidden) {
+ this.tabsHidden = tabsHidden;
+ requestRepaint();
+ }
+
+ /**
+ * Gets tab caption. The tab is identified by the tab content component.
+ *
+ * @param c
+ * the component in the tab
+ * @deprecated Use {@link #getTab(Component)} and {@link Tab#getCaption()}
+ * instead.
+ */
+ @Deprecated
+ public String getTabCaption(Component c) {
+ Tab info = tabs.get(c);
+ if (info == null) {
+ return "";
+ } else {
+ return info.getCaption();
+ }
+ }
+
+ /**
+ * Sets tab caption. The tab is identified by the tab content component.
+ *
+ * @param c
+ * the component in the tab
+ * @param caption
+ * the caption to set.
+ * @deprecated Use {@link #getTab(Component)} and
+ * {@link Tab#setCaption(String)} instead.
+ */
+ @Deprecated
+ public void setTabCaption(Component c, String caption) {
+ Tab info = tabs.get(c);
+ if (info != null) {
+ info.setCaption(caption);
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Gets the icon for a tab. The tab is identified by the tab content
+ * component.
+ *
+ * @param c
+ * the component in the tab
+ * @deprecated Use {@link #getTab(Component)} and {@link Tab#getIcon()}
+ * instead.
+ */
+ @Deprecated
+ public Resource getTabIcon(Component c) {
+ Tab info = tabs.get(c);
+ if (info == null) {
+ return null;
+ } else {
+ return info.getIcon();
+ }
+ }
+
+ /**
+ * Sets icon for the given component. The tab is identified by the tab
+ * content component.
+ *
+ * @param c
+ * the component in the tab
+ * @param icon
+ * the icon to set
+ * @deprecated Use {@link #getTab(Component)} and
+ * {@link Tab#setIcon(Resource)} instead.
+ */
+ @Deprecated
+ public void setTabIcon(Component c, Resource icon) {
+ Tab info = tabs.get(c);
+ if (info != null) {
+ info.setIcon(icon);
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Returns the {@link Tab} (metadata) for a component. The {@link Tab}
+ * object can be used for setting caption,icon, etc for the tab.
+ *
+ * @param c
+ * the component
+ * @return The tab instance associated with the given component, or null if
+ * the tabsheet does not contain the component.
+ */
+ public Tab getTab(Component c) {
+ return tabs.get(c);
+ }
+
+ /**
+ * Returns the {@link Tab} (metadata) for a component. The {@link Tab}
+ * object can be used for setting caption,icon, etc for the tab.
+ *
+ * @param position
+ * the position of the tab
+ * @return The tab in the given position, or null if the position is out of
+ * bounds.
+ */
+ public Tab getTab(int position) {
+ if (position >= 0 && position < getComponentCount()) {
+ return getTab(components.get(position));
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the selected tab. The tab is identified by the tab content
+ * component. Does nothing if the tabsheet doesn't contain the component.
+ *
+ * @param c
+ */
+ public void setSelectedTab(Component c) {
+ if (c != null && components.contains(c) && !c.equals(selected)) {
+ setSelected(c);
+ updateSelection();
+ fireSelectedTabChange();
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Sets the selected tab in the TabSheet. Ensures that the selected tab is
+ * repainted if needed.
+ *
+ * @param c
+ * The new selection or null for no selection
+ */
+ private void setSelected(Component c) {
+ selected = c;
+ // Repaint of the selected component is needed as only the selected
+ // component is communicated to the client. Otherwise this will be a
+ // "cached" update even though the client knows nothing about the
+ // connector
+ if (selected instanceof ComponentContainer) {
+ ((ComponentContainer) selected).requestRepaintAll();
+ } else if (selected instanceof Table) {
+ // Workaround until there's a generic way of telling a component
+ // that there is no client side state to rely on. See #8642
+ ((Table) selected).refreshRowCache();
+ } else if (selected != null) {
+ selected.requestRepaint();
+ }
+
+ }
+
+ /**
+ * Sets the selected tab. The tab is identified by the corresponding
+ * {@link Tab Tab} instance. Does nothing if the tabsheet doesn't contain
+ * the given tab.
+ *
+ * @param tab
+ */
+ public void setSelectedTab(Tab tab) {
+ if (tab != null) {
+ setSelectedTab(tab.getComponent());
+ }
+ }
+
+ /**
+ * Sets the selected tab, identified by its position. Does nothing if the
+ * position is out of bounds.
+ *
+ * @param position
+ */
+ public void setSelectedTab(int position) {
+ setSelectedTab(getTab(position));
+ }
+
+ /**
+ * Checks if the current selection is valid, and updates the selection if
+ * the previously selected component is not visible and enabled. The first
+ * visible and enabled tab is selected if the current selection is empty or
+ * invalid.
+ *
+ * This method does not fire tab change events, but the caller should do so
+ * if appropriate.
+ *
+ * @return true if selection was changed, false otherwise
+ */
+ private boolean updateSelection() {
+ Component originalSelection = selected;
+ for (final Iterator<Component> i = getComponentIterator(); i.hasNext();) {
+ final Component component = i.next();
+
+ Tab tab = tabs.get(component);
+
+ /*
+ * If we have no selection, if the current selection is invisible or
+ * if the current selection is disabled (but the whole component is
+ * not) we select this tab instead
+ */
+ Tab selectedTabInfo = null;
+ if (selected != null) {
+ selectedTabInfo = tabs.get(selected);
+ }
+ if (selected == null || selectedTabInfo == null
+ || !selectedTabInfo.isVisible()
+ || !selectedTabInfo.isEnabled()) {
+
+ // The current selection is not valid so we need to change
+ // it
+ if (tab.isEnabled() && tab.isVisible()) {
+ setSelected(component);
+ break;
+ } else {
+ /*
+ * The current selection is not valid but this tab cannot be
+ * selected either.
+ */
+ setSelected(null);
+ }
+ }
+ }
+ return originalSelection != selected;
+ }
+
+ /**
+ * Gets the selected tab content component.
+ *
+ * @return the selected tab contents
+ */
+ public Component getSelectedTab() {
+ return selected;
+ }
+
+ // inherits javadoc
+
+ @Override
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ if (variables.containsKey("selected")) {
+ setSelectedTab(keyMapper.get((String) variables.get("selected")));
+ }
+ if (variables.containsKey("close")) {
+ final Component tab = keyMapper
+ .get((String) variables.get("close"));
+ if (tab != null) {
+ closeHandler.onTabClose(this, tab);
+ }
+ }
+ if (variables.containsKey(FocusEvent.EVENT_ID)) {
+ fireEvent(new FocusEvent(this));
+ }
+ if (variables.containsKey(BlurEvent.EVENT_ID)) {
+ fireEvent(new BlurEvent(this));
+ }
+ }
+
+ /**
+ * Replaces a component (tab content) with another. This can be used to
+ * change tab contents or to rearrange tabs. The tab position and some
+ * metadata are preserved when moving components within the same
+ * {@link TabSheet}.
+ *
+ * If the oldComponent is not present in the tab sheet, the new one is added
+ * at the end.
+ *
+ * If the oldComponent is already in the tab sheet but the newComponent
+ * isn't, the old tab is replaced with a new one, and the caption and icon
+ * of the old one are copied to the new tab.
+ *
+ * If both old and new components are present, their positions are swapped.
+ *
+ * {@inheritDoc}
+ */
+
+ @Override
+ public void replaceComponent(Component oldComponent, Component newComponent) {
+
+ if (selected == oldComponent) {
+ // keep selection w/o selectedTabChange event
+ setSelected(newComponent);
+ }
+
+ Tab newTab = tabs.get(newComponent);
+ Tab oldTab = tabs.get(oldComponent);
+
+ // Gets the locations
+ int oldLocation = -1;
+ int newLocation = -1;
+ int location = 0;
+ for (final Iterator<Component> i = components.iterator(); i.hasNext();) {
+ final Component component = i.next();
+
+ if (component == oldComponent) {
+ oldLocation = location;
+ }
+ if (component == newComponent) {
+ newLocation = location;
+ }
+
+ location++;
+ }
+
+ if (oldLocation == -1) {
+ addComponent(newComponent);
+ } else if (newLocation == -1) {
+ removeComponent(oldComponent);
+ newTab = addTab(newComponent, oldLocation);
+ // Copy all relevant metadata to the new tab (#8793)
+ // TODO Should reuse the old tab instance instead?
+ copyTabMetadata(oldTab, newTab);
+ } else {
+ components.set(oldLocation, newComponent);
+ components.set(newLocation, oldComponent);
+
+ // Tab associations are not changed, but metadata is swapped between
+ // the instances
+ // TODO Should reassociate the instances instead?
+ Tab tmp = new TabSheetTabImpl(null, null);
+ copyTabMetadata(newTab, tmp);
+ copyTabMetadata(oldTab, newTab);
+ copyTabMetadata(tmp, oldTab);
+
+ requestRepaint();
+ }
+
+ }
+
+ /* Click event */
+
+ private static final Method SELECTED_TAB_CHANGE_METHOD;
+ static {
+ try {
+ SELECTED_TAB_CHANGE_METHOD = SelectedTabChangeListener.class
+ .getDeclaredMethod("selectedTabChange",
+ new Class[] { SelectedTabChangeEvent.class });
+ } catch (final java.lang.NoSuchMethodException e) {
+ // This should never happen
+ throw new java.lang.RuntimeException(
+ "Internal error finding methods in TabSheet");
+ }
+ }
+
+ /**
+ * Selected tab change event. This event is sent when the selected (shown)
+ * tab in the tab sheet is changed.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+ public class SelectedTabChangeEvent extends Component.Event {
+
+ /**
+ * New instance of selected tab change event
+ *
+ * @param source
+ * the Source of the event.
+ */
+ public SelectedTabChangeEvent(Component source) {
+ super(source);
+ }
+
+ /**
+ * TabSheet where the event occurred.
+ *
+ * @return the Source of the event.
+ */
+ public TabSheet getTabSheet() {
+ return (TabSheet) getSource();
+ }
+ }
+
+ /**
+ * Selected tab change event listener. The listener is called whenever
+ * another tab is selected, including when adding the first tab to a
+ * tabsheet.
+ *
+ * @author Vaadin Ltd.
+ *
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+ public interface SelectedTabChangeListener extends Serializable {
+
+ /**
+ * Selected (shown) tab in tab sheet has has been changed.
+ *
+ * @param event
+ * the selected tab change event.
+ */
+ public void selectedTabChange(SelectedTabChangeEvent event);
+ }
+
+ /**
+ * Adds a tab selection listener
+ *
+ * @param listener
+ * the Listener to be added.
+ */
+ public void addListener(SelectedTabChangeListener listener) {
+ addListener(SelectedTabChangeEvent.class, listener,
+ SELECTED_TAB_CHANGE_METHOD);
+ }
+
+ /**
+ * Removes a tab selection listener
+ *
+ * @param listener
+ * the Listener to be removed.
+ */
+ public void removeListener(SelectedTabChangeListener listener) {
+ removeListener(SelectedTabChangeEvent.class, listener,
+ SELECTED_TAB_CHANGE_METHOD);
+ }
+
+ /**
+ * Sends an event that the currently selected tab has changed.
+ */
+ protected void fireSelectedTabChange() {
+ fireEvent(new SelectedTabChangeEvent(this));
+ }
+
+ /**
+ * Tab meta-data for a component in a {@link TabSheet}.
+ *
+ * The meta-data includes the tab caption, icon, visibility and enabledness,
+ * closability, description (tooltip) and an optional component error shown
+ * in the tab.
+ *
+ * Tabs are identified by the component contained on them in most cases, and
+ * the meta-data can be obtained with {@link TabSheet#getTab(Component)}.
+ */
+ public interface Tab extends Serializable {
+ /**
+ * Returns the visible status for the tab. An invisible tab is not shown
+ * in the tab bar and cannot be selected.
+ *
+ * @return true for visible, false for hidden
+ */
+ public boolean isVisible();
+
+ /**
+ * Sets the visible status for the tab. An invisible tab is not shown in
+ * the tab bar and cannot be selected, selection is changed
+ * automatically when there is an attempt to select an invisible tab.
+ *
+ * @param visible
+ * true for visible, false for hidden
+ */
+ public void setVisible(boolean visible);
+
+ /**
+ * Returns the closability status for the tab.
+ *
+ * @return true if the tab is allowed to be closed by the end user,
+ * false for not allowing closing
+ */
+ public boolean isClosable();
+
+ /**
+ * Sets the closability status for the tab. A closable tab can be closed
+ * by the user through the user interface. This also controls if a close
+ * button is shown to the user or not.
+ * <p>
+ * Note! Currently only supported by TabSheet, not Accordion.
+ * </p>
+ *
+ * @param visible
+ * true if the end user is allowed to close the tab, false
+ * for not allowing to close. Should default to false.
+ */
+ public void setClosable(boolean closable);
+
+ /**
+ * Returns the enabled status for the tab. A disabled tab is shown as
+ * such in the tab bar and cannot be selected.
+ *
+ * @return true for enabled, false for disabled
+ */
+ public boolean isEnabled();
+
+ /**
+ * Sets the enabled status for the tab. A disabled tab is shown as such
+ * in the tab bar and cannot be selected.
+ *
+ * @param enabled
+ * true for enabled, false for disabled
+ */
+ public void setEnabled(boolean enabled);
+
+ /**
+ * Sets the caption for the tab.
+ *
+ * @param caption
+ * the caption to set
+ */
+ public void setCaption(String caption);
+
+ /**
+ * Gets the caption for the tab.
+ */
+ public String getCaption();
+
+ /**
+ * Gets the icon for the tab.
+ */
+ public Resource getIcon();
+
+ /**
+ * Sets the icon for the tab.
+ *
+ * @param icon
+ * the icon to set
+ */
+ public void setIcon(Resource icon);
+
+ /**
+ * Gets the description for the tab. The description can be used to
+ * briefly describe the state of the tab to the user, and is typically
+ * shown as a tooltip when hovering over the tab.
+ *
+ * @return the description for the tab
+ */
+ public String getDescription();
+
+ /**
+ * Sets the description for the tab. The description can be used to
+ * briefly describe the state of the tab to the user, and is typically
+ * shown as a tooltip when hovering over the tab.
+ *
+ * @param description
+ * the new description string for the tab.
+ */
+ public void setDescription(String description);
+
+ /**
+ * Sets an error indicator to be shown in the tab. This can be used e.g.
+ * to communicate to the user that there is a problem in the contents of
+ * the tab.
+ *
+ * @see AbstractComponent#setComponentError(ErrorMessage)
+ *
+ * @param componentError
+ * error message or null for none
+ */
+ public void setComponentError(ErrorMessage componentError);
+
+ /**
+ * Gets the current error message shown for the tab.
+ *
+ * TODO currently not sent to the client
+ *
+ * @see AbstractComponent#setComponentError(ErrorMessage)
+ */
+ public ErrorMessage getComponentError();
+
+ /**
+ * Get the component related to the Tab
+ */
+ public Component getComponent();
+
+ /**
+ * Sets a style name for the tab. The style name will be rendered as a
+ * HTML class name, which can be used in a CSS definition.
+ *
+ * <pre>
+ * Tab tab = tabsheet.addTab(tabContent, &quot;Tab text&quot;);
+ * tab.setStyleName(&quot;mystyle&quot;);
+ * </pre>
+ * <p>
+ * The used style name will be prefixed with "
+ * {@code v-tabsheet-tabitemcell-}". For example, if you give a tab the
+ * style "{@code mystyle}", the tab will get a "
+ * {@code v-tabsheet-tabitemcell-mystyle}" style. You could then style
+ * the component with:
+ * </p>
+ *
+ * <pre>
+ * .v-tabsheet-tabitemcell-mystyle {font-style: italic;}
+ * </pre>
+ *
+ * <p>
+ * This method will trigger a {@link RepaintRequestEvent} on the
+ * TabSheet to which the Tab belongs.
+ * </p>
+ *
+ * @param styleName
+ * the new style to be set for tab
+ * @see #getStyleName()
+ */
+ public void setStyleName(String styleName);
+
+ /**
+ * Gets the user-defined CSS style name of the tab. Built-in style names
+ * defined in Vaadin or GWT are not returned.
+ *
+ * @return the style name or of the tab
+ * @see #setStyleName(String)
+ */
+ public String getStyleName();
+ }
+
+ /**
+ * TabSheet's implementation of {@link Tab} - tab metadata.
+ */
+ public class TabSheetTabImpl implements Tab {
+
+ private String caption = "";
+ private Resource icon = null;
+ private boolean enabled = true;
+ private boolean visible = true;
+ private boolean closable = false;
+ private String description = null;
+ private ErrorMessage componentError = null;
+ private String styleName;
+
+ public TabSheetTabImpl(String caption, Resource icon) {
+ if (caption == null) {
+ caption = "";
+ }
+ this.caption = caption;
+ this.icon = icon;
+ }
+
+ /**
+ * Returns the tab caption. Can never be null.
+ */
+
+ @Override
+ public String getCaption() {
+ return caption;
+ }
+
+ @Override
+ public void setCaption(String caption) {
+ this.caption = caption;
+ requestRepaint();
+ }
+
+ @Override
+ public Resource getIcon() {
+ return icon;
+ }
+
+ @Override
+ public void setIcon(Resource icon) {
+ this.icon = icon;
+ requestRepaint();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ if (updateSelection()) {
+ fireSelectedTabChange();
+ }
+ requestRepaint();
+ }
+
+ @Override
+ public boolean isVisible() {
+ return visible;
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ if (updateSelection()) {
+ fireSelectedTabChange();
+ }
+ requestRepaint();
+ }
+
+ @Override
+ public boolean isClosable() {
+ return closable;
+ }
+
+ @Override
+ public void setClosable(boolean closable) {
+ this.closable = closable;
+ requestRepaint();
+ }
+
+ public void close() {
+
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public void setDescription(String description) {
+ this.description = description;
+ requestRepaint();
+ }
+
+ @Override
+ public ErrorMessage getComponentError() {
+ return componentError;
+ }
+
+ @Override
+ public void setComponentError(ErrorMessage componentError) {
+ this.componentError = componentError;
+ requestRepaint();
+ }
+
+ @Override
+ public Component getComponent() {
+ for (Map.Entry<Component, Tab> entry : tabs.entrySet()) {
+ if (entry.getValue() == this) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setStyleName(String styleName) {
+ this.styleName = styleName;
+ requestRepaint();
+ }
+
+ @Override
+ public String getStyleName() {
+ return styleName;
+ }
+ }
+
+ /**
+ * CloseHandler is used to process tab closing events. Default behavior is
+ * to remove the tab from the TabSheet.
+ *
+ * @author Jouni Koivuviita / Vaadin Ltd.
+ * @since 6.2.0
+ *
+ */
+ public interface CloseHandler extends Serializable {
+
+ /**
+ * Called when a user has pressed the close icon of a tab in the client
+ * side widget.
+ *
+ * @param tabsheet
+ * the TabSheet to which the tab belongs to
+ * @param tabContent
+ * the component that corresponds to the tab whose close
+ * button was clicked
+ */
+ void onTabClose(final TabSheet tabsheet, final Component tabContent);
+ }
+
+ /**
+ * Provide a custom {@link CloseHandler} for this TabSheet if you wish to
+ * perform some additional tasks when a user clicks on a tabs close button,
+ * e.g. show a confirmation dialogue before removing the tab.
+ *
+ * To remove the tab, if you provide your own close handler, you must call
+ * {@link #removeComponent(Component)} yourself.
+ *
+ * The default CloseHandler for TabSheet will only remove the tab.
+ *
+ * @param handler
+ */
+ public void setCloseHandler(CloseHandler handler) {
+ closeHandler = handler;
+ }
+
+ /**
+ * Sets the position of the tab.
+ *
+ * @param tab
+ * The tab
+ * @param position
+ * The new position of the tab
+ */
+ public void setTabPosition(Tab tab, int position) {
+ int oldPosition = getTabPosition(tab);
+ components.remove(oldPosition);
+ components.add(position, tab.getComponent());
+ requestRepaint();
+ }
+
+ /**
+ * Gets the position of the tab
+ *
+ * @param tab
+ * The tab
+ * @return
+ */
+ public int getTabPosition(Tab tab) {
+ return components.indexOf(tab.getComponent());
+ }
+
+ @Override
+ public void focus() {
+ super.focus();
+ }
+
+ @Override
+ public int getTabIndex() {
+ return tabIndex;
+ }
+
+ @Override
+ public void setTabIndex(int tabIndex) {
+ this.tabIndex = tabIndex;
+ requestRepaint();
+ }
+
+ @Override
+ public void addListener(BlurListener listener) {
+ addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
+ BlurListener.blurMethod);
+ }
+
+ @Override
+ public void removeListener(BlurListener listener) {
+ removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
+ }
+
+ @Override
+ public void addListener(FocusListener listener) {
+ addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
+ FocusListener.focusMethod);
+ }
+
+ @Override
+ public void removeListener(FocusListener listener) {
+ removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
+
+ }
+
+ @Override
+ public boolean isComponentVisible(Component childComponent) {
+ return childComponent == getSelectedTab();
+ }
+
+ /**
+ * Copies properties from one Tab to another.
+ *
+ * @param from
+ * The tab whose data to copy.
+ * @param to
+ * The tab to which copy the data.
+ */
+ private static void copyTabMetadata(Tab from, Tab to) {
+ to.setCaption(from.getCaption());
+ to.setIcon(from.getIcon());
+ to.setDescription(from.getDescription());
+ to.setVisible(from.isVisible());
+ to.setEnabled(from.isEnabled());
+ to.setClosable(from.isClosable());
+ to.setStyleName(from.getStyleName());
+ to.setComponentError(from.getComponentError());
+ }
+}