package org.limewire.ui.swing.components;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.painter.Painter;
/**
* A base container for {@link FancyTab FancyTab} objects. Each tab is
* represented by a {@link TabActionMap TabActionMap} that defines the actions
* taken when a tab is selected. Concrete subclasses should specify their own
* layout managers and implement {@link #layoutTabs() layoutTabs()} to add
* visible tabs to the container.
*
* @see FancyTabList
* @see FlexibleTabList
*/
public abstract class AbstractTabList extends JXPanel {
private final List<FancyTab> tabs = new ArrayList<FancyTab>();
private final ButtonGroup tabGroup = new ButtonGroup();
private final FancyTabProperties props = new FancyTabProperties();
/**
* Constructs an empty AbstractTabList.
*/
protected AbstractTabList() {
}
/**
* Sets a new list of tabs using the specified collection of action maps.
* Each action map in the collection is assigned to a separate tab. The
* method calls <code>layoutTabs()</code> to display the visible tabs.
*/
public void setTabActionMaps(Iterable<? extends TabActionMap> newActionMaps) {
// Remove existing tabs.
for (FancyTab tab : tabs) {
tab.removeFromGroup(tabGroup);
}
tabs.clear();
// Add new tabs.
for (TabActionMap actions : newActionMaps) {
FancyTab tab = createAndPrepareTab(actions);
tabs.add(tab);
}
// Layout tabs in container.
layoutTabs();
}
/**
* Creates a new tab with the specified action map.
*/
protected FancyTab createAndPrepareTab(TabActionMap actionMap) {
// Create tab.
final FancyTab tab = new FancyTab(actionMap, tabGroup, props);
// Add listener to remove tab.
tab.addRemoveActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
removeTab(tab);
}
});
// Add listener to update layout when tab selected.
actionMap.getMainAction().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Action.SELECTED_KEY)) {
if (evt.getNewValue().equals(Boolean.TRUE)) {
layoutTabs();
}
}
}
});
return tab;
}
/**
* Updates the layout to display the visible tabs.
*/
protected abstract void layoutTabs();
/**
* Adds the specified tab to the list at the specified index. This method
* also calls <code>layoutTabs()</code> to update the visible tabs.
*/
protected void addTab(FancyTab tab, int i) {
tabs.add(i, tab);
layoutTabs();
}
/**
* Removes the specified tab from the container. This does not trigger
* any listeners on the tab's removal. This method may call
* <code>layoutTabs()</code> to update the visible tabs.
*/
protected void removeTab(FancyTab tab) {
boolean selected = tab.isSelected();
int idx = tabs.indexOf(tab);
assert idx != -1;
tabs.remove(tab);
tab.removeFromGroup(tabGroup);
// Shift the selection to the tab to the left (or right, if idx==0)
if (selected && !tabs.isEmpty()) {
// Selecting a tab will trigger a layout.
if (idx == 0 && tabs.size() > 0) {
tabs.get(0).getTabActionMap().getMainAction().putValue(Action.SELECTED_KEY, true);
} else if (idx > 0 && tabs.size() > 0) {
tabs.get(idx - 1).getTabActionMap().getMainAction().putValue(Action.SELECTED_KEY, true);
} // else empty, no need to layout.
} else {
layoutTabs();
}
}
/**
* Removes the tab associated with the specified action map. This will not
* trigger an action from the {@link TabActionMap#getRemoveAction()} action.
*/
public void removeTabActionMap(TabActionMap actionMap) {
for (Iterator<FancyTab> iter = tabs.iterator(); iter.hasNext(); ) {
FancyTab tab = iter.next();
if (tab.getTabActionMap().equals(actionMap)) {
tab.removeFromGroup(tabGroup);
iter.remove();
break;
}
}
layoutTabs();
}
/**
* Returns the currently selected tab.
*/
public FancyTab getSelectedTab() {
for (FancyTab tab : tabs) {
if (tab.isSelected()) {
return tab;
}
}
return null;
}
/**
* Returns the tab properties that apply to the tab list.
*/
public FancyTabProperties getTabProperties() {
return props;
}
/**
* Returns an unmodifiable list of all tabs.
*/
public List<FancyTab> getTabs() {
return Collections.unmodifiableList(tabs);
}
/**
* Sets the painter to be used when the tab is rolled over.
*/
public void setHighlightPainter(Painter<?> highlightPainter) {
for (FancyTab tab : tabs) {
if (tab.isHighlighted()) {
tab.setBackgroundPainter(highlightPainter);
}
}
props.setHighlightPainter(highlightPainter);
}
/**
* Sets the painter to be used when the tab is selected.
*/
public void setSelectionPainter(Painter<?> selectedPainter) {
for (FancyTab tab : tabs) {
if (tab.isSelected()) {
tab.setBackgroundPainter(selectedPainter);
}
}
props.setSelectedPainter(selectedPainter);
}
/**
* Sets the color used to render the tab's text when it is not selected.
*/
public void setTabTextColor(Color normalColor) {
for (FancyTab tab : tabs) {
if (!tab.isSelected()) {
tab.setButtonForeground(normalColor);
}
}
props.setNormalColor(normalColor);
}
/**
* Sets the color used to render the tab's text when it is selected.
*/
public void setTabTextSelectedColor(Color selectionColor) {
for (FancyTab tab : tabs) {
if (tab.isSelected()) {
tab.setButtonForeground(selectionColor);
}
}
props.setSelectionColor(selectionColor);
}
/**
* Sets the font used to render the tab's text.
*/
public void setTextFont(Font font) {
for (FancyTab tab : tabs) {
tab.setTextFont(font);
}
props.setTextFont(font);
}
}