echopointng.TabbedPane Maven / Gradle / Ivy
Show all versions of ibis-echo2 Show documentation
/*
* This file is part of the Echo Point Project. This project is a collection
* of Components that have extended the Echo Web Application Framework.
*
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*/
package echopointng;
import java.io.Serializable;
import nextapp.echo2.app.Alignment;
import nextapp.echo2.app.Color;
import nextapp.echo2.app.Component;
import nextapp.echo2.app.Extent;
import nextapp.echo2.app.PaneContainer;
import nextapp.echo2.app.Style;
import nextapp.echo2.app.button.AbstractButton;
import nextapp.echo2.app.event.ActionEvent;
import nextapp.echo2.app.event.ActionListener;
import nextapp.echo2.app.event.ChangeEvent;
import nextapp.echo2.app.event.ChangeListener;
import echopointng.able.ScrollBarProperties;
import echopointng.able.Scrollable;
import echopointng.able.Stretchable;
import echopointng.model.DefaultSingleSelectionModel;
import echopointng.model.SingleSelectionModel;
import echopointng.tabbedpane.DefaultTabModel;
import echopointng.tabbedpane.TabModel;
import echopointng.util.ComponentTracker;
import echopointng.util.reflect.ReflectionKit;
/**
* TabbedPane
is used to provide a tabbed interface to a
* collection of Component
s.
*
* A TabModel
is used to provide the collections of tabs and the
* content that should be shown when a given tab is selected.
*
* A SingleSelectionModel
is used to track which tab is currently
* selected.
*
* This component is a PaneContainer
and hence can have
* components that implement Pane
as a child. However many
* Pane
s, such as SplitPane
, require a definite
* height to be set in order to work properly. So make sure you call setHeight()
* if one of the children implements Pane
*
*/
public class TabbedPane extends AbleComponent implements PaneContainer, Stretchable, Scrollable {
/**
* TabActionListener
listens to any Tab components for action
* events and then sets the tabs selection based on whcih one raised the
* event.
*/
private class TabActionListener implements ActionListener, Serializable {
/**
* @see nextapp.echo2.app.event.ActionListener#actionPerformed(nextapp.echo2.app.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof Component) {
Component tabComponent = (Component) e.getSource();
int index = getModel().indexOfTab(tabComponent);
if (index != -1) {
getSelectionModel().setSelectedIndex(index);
}
firePropertyChange(null, null, null);
}
}
}
/**
* TabModelListener
is used to track changes in the TabModel
* and reflect these back in the
*/
private class TabModelListener implements ChangeListener, Serializable {
/**
* @see nextapp.echo2.app.event.ChangeListener#stateChanged(nextapp.echo2.app.event.ChangeEvent)
*/
public void stateChanged(ChangeEvent e) {
TabModel model = getModel();
SingleSelectionModel selectionModel = getSelectionModel();
int selectedIndex = getSelectionModel().getSelectedIndex();
//
// remove all children in the TabbedPane cause things have changed
// in the underlying model
removeAllTabContent();
removeAllTabComponents();
int newTabCount = model.size();
if (newTabCount <= 0) {
// we have no atbs and hence no selection
selectionModel.setSelectedIndex(-1);
} else {
if (selectedIndex >= newTabCount) {
// we have some tabs but the selection could still be after
// its new length
selectedIndex = newTabCount - 1;
} else if (selectedIndex < 0) {
// or not selected at all
selectedIndex = 0;
}
selectionModel.setSelectedIndex(selectedIndex);
//
// now find the component at this index and add it
// if it not already a child
Component tabContent = model.getTabContentAt(selectedIndex);
if (!isAncestorOf(tabContent)) {
contentTracker.add(tabContent);
}
firePropertyChange(null, null, null);
}
}
}
/**
* TabSelectionListener
is used to track component selection
* and hence tell the model when old content can be released and add new
* content into the TabbedPane.
*/
private class TabSelectionListener implements ChangeListener, Serializable {
/**
* @see nextapp.echo2.app.event.ChangeListener#stateChanged(nextapp.echo2.app.event.ChangeEvent)
*/
public void stateChanged(ChangeEvent e) {
SingleSelectionModel srcSelectionModel = (SingleSelectionModel) e.getSource();
TabModel model = getModel();
int newSelectedIndex = srcSelectionModel.getSelectedIndex();
//
// remove old content
removeAllTabContent();
if (currentlySelectedIndex >= 0) {
model.releaseTabAt(currentlySelectedIndex);
}
//
// add the new content
if (newSelectedIndex >= 0) {
Component newContent = model.getTabContentAt(newSelectedIndex);
if (newContent != null) {
contentTracker.add(newContent);
}
}
currentlySelectedIndex = newSelectedIndex;
firePropertyChange(null, null, null);
}
}
// ---------------------------------------------------------------
// ---------------------------------------------------------------
public static final Style DEFAULT_STYLE;
public static final String PROPERTY_MODEL = "model";
public static final String PROPERTY_SELECTION_MODEL = "selectionModel";
public static final String PROPERTY_TAB_BORDER_STYLE = "tabBorderStyle";
public static final String PROPERTY_TAB_LEAD_IN_WIDTH = "tabLeadInWidth";
public static final String PROPERTY_TAB_PLACEMENT = "tabPlacement";
public static final String PROPERTY_TAB_SPACING = "tabSpacing";
/**
* TAB_LINE_AND_CONTENT - shows a border around the tabs and a line betweent
* the tabs and the content
*
* It looks a bit like this (where ==== is the border and --- is the
* non-border area
*
*
* ----------- ----------- ----------- -----------
* - - - - - - - -
* - - - - - - - -
* ============================ ==========================
* = =
* = =
* = =
* = =
* = =
* = =
* ===============================================================
*
*/
public static final int TAB_LINE_AND_CONTENT = 1;
/**
* TAB_LINE_ONLY - shows no border around the tabs and a line betweent the
* tabs and the content
*
* It looks a bit like this (where ==== is the border and --- is the
* non-border area
*
*
* ----------- ----------- ----------- -----------
* - - - - - - - -
* - - - - - - - -
* ============================ ==========================
* - -
* - -
* - -
* - -
* - -
* - -
* ---------------------------------------------------------------
*
*/
public static final int TAB_LINE_ONLY = 2;
/**
* TAB_STRIP_AND_CONTENT - shows the border around the tabs and content
*
* It looks a bit like this (where ==== is the border and --- is the
* non-border area
*
*
* =========== =========== =========== ===========
* = = = = = = = =
* = = = = = = = =
* ============================ ==========================
* = =
* = =
* = =
* = =
* = =
* = =
* ===============================================================
*
*/
public static final int TAB_STRIP_AND_CONTENT = 3;
/**
* TAB_STRIP_ONLY - shows a border around the tabs and a line betweent the
* tabs and the content
*
* It looks a bit like this (where ==== is the border and --- is the
* non-border area
*
*
* =========== =========== =========== ===========
* = = = = = = = =
* = = = = = = = =
* ============================ ==========================
* - -
* - -
* - -
* - -
* - -
* - -
* ---------------------------------------------------------------
*
*
*/
public static final int TAB_STRIP_ONLY = 4;
static {
MutableStyleEx style = new MutableStyleEx();
style.setProperty(PROPERTY_TAB_PLACEMENT, Alignment.TOP);
style.setProperty(PROPERTY_TAB_SPACING, 5);
style.setProperty(PROPERTY_BORDER, new BorderEx(1));
style.setProperty(PROPERTY_TAB_BORDER_STYLE, TAB_STRIP_AND_CONTENT);
DEFAULT_STYLE = style;
}
/** used to contain the tab content components */
private ComponentTracker contentTracker = new ComponentTracker(this);
/** our currently selected index */
private int currentlySelectedIndex = -1;
private TabActionListener internalActionListener = new TabActionListener();
private TabModelListener internalModelListener = new TabModelListener();
private TabSelectionListener internalSelectionListener = new TabSelectionListener();
/** used to contain the tab components */
private ComponentTracker tabsTracker = new ComponentTracker(this);
/**
* Constructs a TabbedPane
with the a default empty
* DefaultTabModel
as the TabbedPane's model.
*
* You can retrieve this as follows to add content to the TabbedPane:
*
*
* TabbedPane tabbedPane = new TabbedPane();
* DefaultTabModel model = (DefaultTabModel) tabbedPane.getModel();
* model.addTab("Tab 1", new ContentThingy());
* model.addTab("Tab 2", new ContentThingo());
*
*
*
*/
public TabbedPane() {
this(new DefaultTabModel());
}
/**
* Constructs a TabbedPane
with the specified
* TabModel
.
*
* @param tabModel -
* the TabModel
to use for this
* TabbedPane
*/
public TabbedPane(TabModel tabModel) {
setSelectionModel(new DefaultSingleSelectionModel());
setModel(tabModel);
// if we have some content
// then we always have a selection
if (tabModel.size() > 0) {
getSelectionModel().setSelectedIndex(0);
}
}
/**
* @return returns the TabModel
used to provide the
* TabbedPane's tabs and content.
*/
public TabModel getModel() {
return (TabModel) getProperty(PROPERTY_MODEL);
}
/**
* @return the currently selected index in the TabbedPane's selection model
*/
public int getSelectedIndex() {
return getSelectionModel().getSelectedIndex();
}
/**
* @return the SingleSelectionModel
used to track the
* selected index of the TabbedPane
*/
public SingleSelectionModel getSelectionModel() {
return (SingleSelectionModel) getProperty(PROPERTY_SELECTION_MODEL);
}
/**
* This can be one of the following values :
*
* - TAB_STRIP_ONLY - shows a border around the tabs and a line betweent
* the tabs and the content
* - TAB_STRIP_AND_CONTENT - shows the border around the tabs and content
* - TAB_LINE_ONLY - shows no border around the tabs and a line betweent
* the tabs and the content
* - TAB_LINE_AND_CONTENT - shows a border around the tabs and a line
* betweent the tabs and the content
*
*
* @return - the current tab border style.
*/
public int getTabBorderStyle() {
return getProperty(PROPERTY_TAB_BORDER_STYLE, TAB_STRIP_AND_CONTENT);
}
/**
* @return lead in region before the first tab placement
*/
public Extent getTabLeadInWidth() {
return (Extent) getProperty(PROPERTY_TAB_LEAD_IN_WIDTH);
}
/**
* Returns where the tabs are placed on the TabbedPane. This can be one of
* the following values :
*
* Alignment.TOP
- the bottom of the TabbedPane
* (default)
* Alignment.BOTTOM
- the bottom of the TabbedPane
*
*
* @return - the current placement of the tabs
*/
public int getTabPlacement() {
return getProperty(PROPERTY_TAB_PLACEMENT, Alignment.TOP);
}
/**
* @return - the spacing (in pixels) between tabs, the default is 5
*/
public int getTabSpacing() {
return getProperty(PROPERTY_TAB_SPACING, 5);
}
/**
* @see nextapp.echo2.app.Component#processInput(java.lang.String,
* java.lang.Object)
*/
public void processInput(String inputName, Object inputValue) {
super.processInput(inputName, inputValue);
if ("click".equals(inputName)) {
int selectedIndex = Integer.parseInt((String) inputValue);
setSelectedIndex(selectedIndex);
firePropertyChange(null, null, null);
}
}
/**
* Removes all tab children and takes away the ActionListener if in place
*/
private void removeAllTabComponents() {
Component[] children = tabsTracker.getComponents();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof AbstractButton) {
((AbstractButton) children[i]).removeActionListener(internalActionListener);
}
}
tabsTracker.removeAll();
}
private void removeAllTabContent() {
contentTracker.removeAll();
}
/**
* Sets the TabModel
used to provide the TabbedPane's tabs
* and content.
*
* @param model -
* the TabModel
used to provide the TabbedPane's
* tabs and content.
*/
public void setModel(TabModel model) {
if (model == null)
throw new IllegalArgumentException("The TabbedPane model must not be null!");
TabModel oldValue = getModel();
if (oldValue != model) {
if (oldValue != null)
oldValue.removeChangeListener(this.internalModelListener);
}
model.addChangeListener(this.internalModelListener);
setProperty(PROPERTY_MODEL, model);
}
/**
* Sets the selected index of the TabbedPane.
*
* Shortcurt method to getSelectionModel().setSelectedIndex(index).
*
* @param index -
* the index to select
*/
public void setSelectedIndex(int index) {
getSelectionModel().setSelectedIndex(index);
}
/**
* Sets the SingleSelectionModel
used to track the selected
* index of the TabbedPane
*
* @param selectionModel -
* the SingleSelectionModel
used to track the
* selected index of the TabbedPane
*/
public void setSelectionModel(SingleSelectionModel selectionModel) {
if (selectionModel == null)
throw new IllegalArgumentException("The TabbedPane selectionModel must not be null!");
SingleSelectionModel oldValue = getSelectionModel();
if (oldValue != selectionModel) {
if (oldValue != null)
oldValue.removeChangeListener(this.internalSelectionListener);
}
selectionModel.addChangeListener(this.internalSelectionListener);
setProperty(PROPERTY_SELECTION_MODEL, selectionModel);
}
/**
* Sets the tab border style to use. This controls how the border is drawn
* around the tabs and content.
*
* This can be one of the following values :
*
* - TAB_STRIP_ONLY - shows a border around the tabs and a line betweent
* the tabs and the content
* - TAB_STRIP_AND_CONTENT - shows the border around the tabs and content
* - TAB_LINE_ONLY - shows no border around the tabs and a line betweent
* the tabs and the content
* - TAB_LINE_AND_CONTENT - shows a border around the tabs and a line
* betweent the tabs and the content
*
*
* @see TabbedPane#TAB_STRIP_ONLY
* @see TabbedPane#TAB_STRIP_AND_CONTENT
* @see TabbedPane#TAB_LINE_ONLY
* @see TabbedPane#TAB_LINE_AND_CONTENT
*
* @param newValue
*/
public void setTabBorderStyle(int newValue) {
switch (newValue) {
case TAB_LINE_AND_CONTENT:
case TAB_LINE_ONLY:
case TAB_STRIP_AND_CONTENT:
case TAB_STRIP_ONLY:
break;
default:
throw new IllegalArgumentException(
"tabBorderStyle must one of TAB_LINE_AND_CONTENT, TAB_LINE_ONLY, TAB_STRIP_AND_CONTENT, TAB_STRIP_ONLY");
}
setProperty(PROPERTY_TAB_BORDER_STYLE, newValue);
}
/**
* Sets the width of a lead in region before the first tab placement
*
* @param newValue -
* lead in region before the first tab placement
*/
public void setTabLeadInWidth(Extent newValue) {
setProperty(PROPERTY_TAB_LEAD_IN_WIDTH, newValue);
}
/**
* Sets where the tabs are placed on the TabbedPane. This can be one of the
* following values :
*
* Alignment.TOP
- the bottom of the TabbedPane
* (default)
* Alignment.BOTTOM
- the bottom of the TabbedPane
*
*
* @param tabPlacement
*/
public void setTabPlacement(int tabPlacement) {
switch (tabPlacement) {
case Alignment.TOP:
case Alignment.BOTTOM:
break;
default:
throw new IllegalArgumentException("The TabbedPane tabPlacement value is invalid : " + tabPlacement);
}
setProperty(PROPERTY_TAB_PLACEMENT, tabPlacement);
}
/**
* Sets the spacing (in pixels) between tabs
*
* @param tabSpacing -
* the spacing (in pixels) between tabs
*/
public void setTabSpacing(int tabSpacing) {
setProperty(PROPERTY_TAB_SPACING, tabSpacing);
}
/**
* @return the size on the TabbedPane's TabModel
*/
public int size() {
return getModel().size();
}
/**
* @see nextapp.echo2.app.Component#validate()
*/
public void validate() {
super.validate();
TabModel model = getModel();
int selectedIndex = getSelectionModel().getSelectedIndex();
int tabCount = model.size();
for (int i = 0; i < tabCount; i++) {
Component tabComponent = model.getTabAt(this,i, i == selectedIndex);
if (tabComponent == null) {
throw new IllegalStateException("There must be a non null tab at index : " + i);
}
//
// ok they must be a child of ours
if (tabComponent != null && !this.isAncestorOf(tabComponent)) {
tabsTracker.add(tabComponent);
}
// are they an actionEvent producer?
ReflectionKit.invokeIfPresent("removeActionListener", new Class[] { ActionListener.class }, null, tabComponent,
new Object[] { internalActionListener });
ReflectionKit.invokeIfPresent("addActionListener", new Class[] { ActionListener.class }, null, tabComponent,
new Object[] { internalActionListener });
if (tabComponent instanceof AbstractButton) {
AbstractButton btn = (AbstractButton) tabComponent;
btn.removeActionListener(internalActionListener);
btn.addActionListener(internalActionListener);
}
}
}
/**
* @see echopointng.able.Stretchable#getMaximumStretchedHeight()
*/
public Extent getMaximumStretchedHeight() {
return (Extent) getProperty(PROPERTY_MAXIMUM_STRETCHED_HEIGHT) ;
}
/**
* @see echopointng.able.Stretchable#getMinimumStretchedHeight()
*/
public Extent getMinimumStretchedHeight() {
return (Extent) getProperty(PROPERTY_MINIMUM_STRETCHED_HEIGHT) ;
}
/**
* @see echopointng.able.Stretchable#isHeightStretched()
*/
public boolean isHeightStretched() {
return getProperty(PROPERTY_HEIGHT_STRETCHED, false);
}
/**
* @see echopointng.able.Stretchable#setHeightStretched(boolean)
*/
public void setHeightStretched(boolean newValue) {
setProperty(PROPERTY_HEIGHT_STRETCHED, newValue);
}
/**
* @see echopointng.able.Stretchable#setMaximumStretchedHeight(nextapp.echo2.app.Extent)
*/
public void setMaximumStretchedHeight(Extent newValue) {
Extent.validate(newValue, Extent.PX);
setProperty(PROPERTY_MAXIMUM_STRETCHED_HEIGHT, newValue);
}
/**
* @see echopointng.able.Stretchable#setMinimumStretchedHeight(nextapp.echo2.app.Extent)
*/
public void setMinimumStretchedHeight(Extent newValue) {
Extent.validate(newValue, Extent.PX);
setProperty(PROPERTY_MINIMUM_STRETCHED_HEIGHT, newValue);
}
/**
* @see echopointng.able.Scrollable#getScrollBarBaseColor()
*/
public Color getScrollBarBaseColor() {
return (Color) getProperty(Scrollable.PROPERTY_SCROLL_BAR_BASE_COLOR);
}
/**
* @see echopointng.able.Scrollable#getScrollBarPolicy()
*/
public int getScrollBarPolicy() {
return getProperty(Scrollable.PROPERTY_SCROLL_BAR_POLICY,Scrollable.AUTO);
}
/**
* @see echopointng.able.Scrollable#getScrollBarProperties()
*/
public ScrollBarProperties getScrollBarProperties() {
return (ScrollBarProperties) getProperty(Scrollable.PROPERTY_SCROLL_BAR_PROPERTIES);
}
/**
* @see echopointng.able.Scrollable#setScrollBarBaseColor(nextapp.echo2.app.Color)
*/
public void setScrollBarBaseColor(Color newValue) {
setProperty(Scrollable.PROPERTY_SCROLL_BAR_BASE_COLOR,newValue);
}
/**
* @see echopointng.able.Scrollable#setScrollBarPolicy(int)
*/
public void setScrollBarPolicy(int newScrollBarPolicy) {
setProperty(Scrollable.PROPERTY_SCROLL_BAR_POLICY,newScrollBarPolicy);
}
/**
* @see echopointng.able.Scrollable#setScrollBarProperties(echopointng.able.ScrollBarProperties)
*/
public void setScrollBarProperties(ScrollBarProperties newValue) {
setProperty(Scrollable.PROPERTY_SCROLL_BAR_PROPERTIES,newValue);
}
}