com.hfg.html.custom.TabbedPaneSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.html.custom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.awt.*;
import com.hfg.html.Div;
import com.hfg.html.HTMLTag;
import com.hfg.html.Span;
import com.hfg.html.Table;
import com.hfg.html.Td;
import com.hfg.html.Tr;
import com.hfg.html.HTML;
import com.hfg.xml.XMLTag;
import com.hfg.xml.XMLUtil;
import com.hfg.graphics.ColorUtil;
import com.hfg.util.Orientation;
//------------------------------------------------------------------------------
/**
Convenience class for constructing a tabbed pane set.
See this page for examples.
Example:
HTMLDoc doc = new HTMLDoc();
HTML html = new HTML();
doc.setRootTag(html);
// Add the CSS classes needed for the tabs
html.getHead().addStyle(TabbedPaneSet.generateCSS());
// Add the javascript needed for the tabs
html.getHead().addJavascript(TabbedPaneSet.generateJavascript());
TabbedPaneSet tabSet1 = new TabbedPaneSet();
tabSet1.addTab("Tab 1", new InputButton("Button 1"));
tabSet1.addTab("Tab 2", new InputText("foo", "bar"));
tabSet1.addTab("Tab 3", new Span("pane3");
Body body = html.getBody();
// Set the onload to initialize the tabs
body.setOnLoad(tabSet1.generateOnLoad());
body.addSubtag(tabSet1);
@author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg XML/HTML Coding Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
public class TabbedPaneSet extends Div
{
private String mSetId;
private Table mLabelTable;
private Div mPaneDiv;
private String mSelectedTabId;
private Orientation mOrientation;
private boolean mRoundedTabs = true;
private static Font sDefaultLabelFont = new Font("Ariel", Font.PLAIN, 10);
private static Color sDefaultSelectedTabColor = Color.decode("#6666ff");
private static Color sDefaultUnselectedTabColor = Color.decode("#c0c0ff");
private static Color sDefaultSelectedTabFontColor = Color.decode("#ffffff");
private static Color sDefaultUnselectedTabFontColor = Color.decode("#2b4353");
private static Orientation sDefaultOrientation = Orientation.HORIZONAL;
private static int sSetCount = 0;
private static int sTabCount = 0;
// CSS classes
private static final String TAB_PANE_SET = "tabPaneSet";
private static final String TAB_LABELS = "tabLabels";
private static final String TAB_PANES = "tabPanes";
private static final String HORIZ_TAB = "tab tab-horiz";
private static final String VERT_TAB = "";
//##########################################################################
// CONSTRUCTORS
//##########################################################################
//--------------------------------------------------------------------------
public TabbedPaneSet()
{
this(sDefaultOrientation);
}
//--------------------------------------------------------------------------
public TabbedPaneSet(Orientation inOrientation)
{
super();
mOrientation = inOrientation;
sSetCount++;
mSetId = "tabPaneSet" + sSetCount + "_" + (horizontal() ? "h" : "v");
setId(mSetId);
// The enclosing table keeps the panes from expanding the width too far.
Table enclosingTable = addTable().setClass(TAB_PANE_SET);
Tr row = enclosingTable.addRow();
Td cell = row.addCell();
mLabelTable = cell.addTable().setClass(TAB_LABELS).setId(mSetId + '_' + TAB_LABELS);
if (horizontal())
{
mLabelTable.addRow();
mPaneDiv = cell.addDiv().setClass(TAB_PANES);
}
else
{
mPaneDiv = row.addCell().addDiv().setClass(TAB_PANES).addDiv();
}
mPaneDiv.setId(mSetId + "_" + TAB_PANES);
}
//##########################################################################
// PUBLIC METHODS
//##########################################################################
//--------------------------------------------------------------------------
public static void setDefaultLabelFont(Font inValue)
{
sDefaultLabelFont = inValue;
}
//--------------------------------------------------------------------------
public static void setDefaultSelectedTabColor(Color inValue)
{
sDefaultSelectedTabColor = inValue;
}
//--------------------------------------------------------------------------
public static void setDefaultUnselectedTabColor(Color inValue)
{
sDefaultUnselectedTabColor = inValue;
}
//--------------------------------------------------------------------------
public static void setDefaultSelectedTabFontColor(Color inValue)
{
sDefaultSelectedTabFontColor = inValue;
}
//--------------------------------------------------------------------------
public static void setDefaultUnselectedTabFontColor(Color inValue)
{
sDefaultUnselectedTabFontColor = inValue;
}
//--------------------------------------------------------------------------
public static String generateCSS()
{
StringBuffer css = new StringBuffer("\n");
css.append(".tab {\n"
+ " background-color: #" + ColorUtil.colorToHex(sDefaultUnselectedTabColor) + ";\n"
+ " color: #" + ColorUtil.colorToHex(sDefaultUnselectedTabFontColor) + ";\n"
+ " text-decoration: none;\n"
+ " cursor: default;\n"
+ " border-right:1px solid #" + ColorUtil.colorToHex(sDefaultSelectedTabColor) + ";\n"
+ " padding: 2px 4px 1px 4px;\n"
+ " margin: 0px 1px;\n"
+ " display: block;\n"
+ " }\n\n");
css.append(".tab-horiz {\n"
+ " margin: 0px 1px;\n"
+ " }\n\n");
css.append(".tab-vert {\n"
+ " margin: 0px 0px;\n"
+ " }\n\n");
css.append(".tab-selected {\n"
+ " background-color: #" + ColorUtil.colorToHex(sDefaultSelectedTabColor) + ";\n"
+ " color: #" + ColorUtil.colorToHex(sDefaultSelectedTabFontColor) + ";\n"
+ " }\n\n");
css.append(".tab:hover {\n"
+ " color: #" + ColorUtil.colorToHex(sDefaultSelectedTabFontColor) + ";\n"
+ " }\n\n");
css.append("table." + TAB_PANE_SET + " {\n"
+ " padding: 0px;\n"
+ " border-collapse: collapse;\n"
// + " table-layout: fixed;\n"
+ " }\n\n");
css.append("table." + TAB_PANE_SET + " td {\n"
+ " padding: 0px;\n"
+ " vertical-align: top\n"
+ " }\n\n");
css.append("table." + TAB_LABELS + " {\n"
+ " font-family: '" + sDefaultLabelFont.getName()
+ (!sDefaultLabelFont.getName().equals(sDefaultLabelFont.getFamily()) ? "', '" + sDefaultLabelFont.getFamily() : "")
+ "', sans-serif;\n"
+ " font-size: " + sDefaultLabelFont.getSize() + "pt;\n"
+ " padding: 0px;\n"
+ " border-collapse: collapse\n"
+ " }\n\n");
css.append("table." + TAB_LABELS + " td {\n"
+ " padding: 0px;\n"
+ " }\n\n");
css.append("." + TAB_PANES + " {\n"
+ " border: solid #" + ColorUtil.colorToHex(sDefaultUnselectedTabColor) + ";\n"
+ " border-width: 1px;\n"
+ " padding: 3px;\n"
+ " }\n\n");
// For rounding the corners of the tabs.
css.append(".rndS1, .rndS2, .rndS3, .rndS4, .rndU1, .rndU2, .rndU3, .rndU4 {\n"
+ " border: 0px;\n"
+ " border-right:1px solid #" + ColorUtil.colorToHex(sDefaultSelectedTabColor) + ";\n"
+ " height: 1px;\n"
+ " padding: 0px;\n"
+ " display: block;\n" /////////////
+ " overflow: hidden;\n" /////////////
+ " }\n\n"
+ ".rndS1, .rndS2, .rndS3, .rndS4 {\n"
+ " background-color: #" + ColorUtil.colorToHex(sDefaultSelectedTabColor) + ";\n"
+ " }\n\n"
+ ".rndU1, .rndU2, .rndU3, .rndU4 {\n"
+ " background-color: #" + ColorUtil.colorToHex(sDefaultUnselectedTabColor) + ";\n"
+ " }\n\n"
+ ".rndHzS4, .rndHzU4 { margin: 0px 1px; height: 1px; }\n"
+ ".rndHzS3, .rndHzU3 { margin: 0px 2px; }\n"
+ ".rndHzS2, .rndHzU2 { margin: 0px 3px; }\n"
+ ".rndHzS1, .rndHzU1 { margin: 0px 5px; }\n"
+ ".rndVertS4, .rndVertU4 { margin: 0px 0px 0px 1px; height: 1px; }\n"
+ ".rndVertS3, .rndVertU3 { margin: 0px 0px 0px 2px; }\n"
+ ".rndVertS2, .rndVertU2 { margin: 0px 0px 0px 3px; }\n"
+ ".rndVertS1, .rndVertU1 { margin: 0px 0px 0px 5px; }\n\n");
// Work around for IE margin issues
// Kinda kludgy but so is IE.
XMLTag styleTag = new XMLTag(HTML.STYLE);
styleTag.setAttribute(HTML.TYPE, "text/css");
css.append(XMLUtil.composeEndTag(styleTag.getTagName())
+ "\n\n\n\n"
+ XMLUtil.composeStartTag(styleTag.getTagName(), styleTag.getAttributes()));
return css.toString();
}
//--------------------------------------------------------------------------
public static String generateJavascript()
{
StringBuffer js = new StringBuffer();
js.append("var tabPaneSets = new Array();\n\n");
js.append("function TabPaneSet()\n"
+ "{\n"
+ " this.labels = new Array();\n"
+ " this.labelEdges = new Array();\n" // Div's for rounded corners
+ " this.panes = new Array();\n"
+ " this.orientation;\n"
+ " this.paneDiv;\n" // Div holding the content panes
+ "}\n\n");
js.append("//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
+ "TabPaneSet.prototype.adjustSize = function()\n"
+ "{\n"
+ " var maxHeight = 0;\n"
+ " var maxWidth = 0;\n"
// Force a recalculation of the nax natural content size
+ " this.paneDiv.style.height = null;\n"
+ " this.paneDiv.style.width = null;\n"
+ " \n"
+ " for (var paneIndex=0; paneIndex < this.panes.length; paneIndex++)\n"
+ " {\n"
+ " var pane = this.panes[paneIndex];\n"
+ " var hiding = false;\n"
+ " if (pane.style.display == 'none')\n"
+ " {\n"
+ " hiding = true;\n"
+ " pane.style.visibility = 'hidden';\n"
+ " pane.style.display = 'block';\n"
+ " }\n"
+ " if (pane.offsetHeight > maxHeight) maxHeight = pane.offsetHeight;\n"
+ " if (pane.offsetWidth > maxWidth) maxWidth = pane.offsetWidth;\n"
// + "alert('offsetWidth: ' + pane.offsetWidth + ', pixelWidth: ' + pane.pixelWidth + ', clientWidth: ' + pane.clientWidth);\n"//////////////////////////////
+ " if (hiding)\n"
+ " {\n"
+ " pane.style.display = 'none';\n"
+ " pane.style.visibility = 'visible';\n"
+ " }\n"
+ " }\n"
+ " \n"
+ " this.paneDiv.style.height = maxHeight + 'px';\n"
+ " this.paneDiv.style.width = maxWidth + 'px';\n"
+ "}\n");
js.append("//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
+ "TabPaneSet.prototype.switchTab = function(inPaneId)\n"
+ "{\n"
+ " // First, hide the unselected panes.\n"
+ " for (var paneIndex=0; paneIndex < this.panes.length; paneIndex++)\n"
+ " {\n"
+ " var pane = this.panes[paneIndex];\n"
+ " if (pane.id != 'pane' + inPaneId) pane.style.display = 'none';\n"
+ " }\n"
+ " \n"
+ " // Now display the selected pane.\n"
+ " for (var paneIndex=0; paneIndex < this.panes.length; paneIndex++)\n"
+ " {\n"
+ " var pane = this.panes[paneIndex];\n"
+ " if (pane.id == 'pane' + inPaneId) pane.style.display = 'block';\n"
+ " }\n"
+ " \n"
+ " for (var labelIndex=0; labelIndex < this.labels.length; labelIndex++)\n"
+ " {\n"
+ " var label = this.labels[labelIndex];\n"
+ " label.className = (label.id == 'tab' + inPaneId ? 'tab tab-selected' : 'tab')\n"
+ " + (this.orientation == 'horiz' ? ' tab-horiz' : ' tab-vert');\n"
+ " for (var i = 0; i < this.labelEdges[labelIndex].length; i++)\n"
+ " {\n"
+ " var suffix = this.labelEdges[labelIndex][i].className.charAt(this.labelEdges[labelIndex][i].className.length - 1);\n"
+ " this.labelEdges[labelIndex][i].className = 'rnd' + (label.id == 'tab' + inPaneId ? 'S': 'U') + suffix\n"
+ " + ' rnd' + (this.orientation == 'horiz' ? 'Hz' : 'Vert')\n"
+ " + (label.id == 'tab' + inPaneId ? 'S': 'U') + suffix;\n"
+ " }\n"
+ " }\n"
+ "}\n\n");
js.append("//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
+ "function setupTabPaneSet(inSetId, inSelectedTabId)\n"
+ "{\n"
+ " var tabPaneSet = new TabPaneSet();\n"
+ " tabPaneSets[inSetId] = tabPaneSet;\n"
+ " tabPaneSet.orientation = (inSetId.match(/_h$/) ? 'horiz' : 'vert');\n"
+ " \n"
+ " // Extract the labels\n"
+ " var labelTable = document.getElementById(inSetId + '_" + TAB_LABELS + "');\n"
+ " var cells = labelTable.getElementsByTagName('td');\n"
+ " for (var cellIndex = 0; cellIndex < cells.length; cellIndex++)\n"
+ " {\n"
+ " var cell = cells[cellIndex];\n"
+ " var labels = cell.getElementsByTagName('span');\n"
+ " for (var i=0; i < labels.length; i++)\n"
+ " {\n"
+ " var label = labels[i];\n"
+ " if (label.id && label.id.indexOf('tab') == 0)\n"
+ " {\n"
+ " tabPaneSet.labels[tabPaneSet.labels.length] = label;\n"
+ " tabPaneSet.labelEdges[tabPaneSet.labelEdges.length] = cell.getElementsByTagName('div');\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " \n"
+ " // Extract the panes\n"
+ " tabPaneSet.paneDiv = document.getElementById(inSetId + '_" + TAB_PANES + "');\n"
+ " var panes = tabPaneSet.paneDiv.getElementsByTagName('div');\n"
+ " for (var paneIndex=0; paneIndex < panes.length; paneIndex++)\n"
+ " {\n"
+ " var pane = panes[paneIndex];\n"
+ " if (pane.parentNode != tabPaneSet.paneDiv || !pane.id || !pane.id.match(/^pane/)) continue;\n"
+ " tabPaneSet.panes[tabPaneSet.panes.length] = pane;\n"
+ " }\n"
+ " \n"
+ " tabPaneSet.adjustSize();\n" // TabbedPaneSet needs to be large enough to accomodate the largest pane.
+ " \n"
+ " document.getElementById(inSelectedTabId).parentNode.onclick();\n"
+ "}\n\n");
js.append("//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
+ "function switchTab(inPaneSetId, inPaneId)\n"
+ "{\n"
+ " var tabPaneSet = tabPaneSets[inPaneSetId];\n"
+ " if (!tabPaneSet)\n"
+ " {\n"
+ " alert('Programming Error!\\ngenerateOnLoad() for this tab pane set was not added to the body.onLoad() during html generation!\\nSee the example in the javadoc.');\n"
+ " return;\n"
+ " }\n"
+ " \n"
+ " tabPaneSet.switchTab(inPaneId);\n"
+ " \n"
+ "}\n\n");
js.append("//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"
+ "function resizeTabPanes(inPaneSetId)\n"
+ "{\n"
+ " var tabPaneSet = tabPaneSets[inPaneSetId];\n"
+ " if (!tabPaneSet)\n"
+ " {\n"
+ " alert('Programming Error!\\ngenerateOnLoad() for this tab pane set was not added to the body.onLoad() during html generation!\\nSee the example in the javadoc.');\n"
+ " return;\n"
+ " }\n"
+ " \n"
+ " tabPaneSet.adjustSize();\n"
+ " \n"
+ "}\n\n");
return js.toString();
}
//--------------------------------------------------------------------------
@Override
public String getId()
{
return mSetId;
}
//--------------------------------------------------------------------------
/**
Tabs are rounded by default. Must be set before addTab() is called.
*/
public TabbedPaneSet useRoundedTabs(boolean inValue)
{
mRoundedTabs = inValue;
return this;
}
//--------------------------------------------------------------------------
/**
* Createds the javascript for the body's onload() that is necessary to
* initialize the tabbed pane set (and any tabbed pane sets contained within it).
*/
public String generateOnLoad()
{
StringBuffer buffer = new StringBuffer();
List tabPaneSets = new ArrayList();
tabPaneSets.add(this);
findNestedTabPaneSets(mPaneDiv, tabPaneSets);
for (int i = tabPaneSets.size() - 1; i >= 0; i--)
{
TabbedPaneSet paneSet = tabPaneSets.get(i);
buffer.append("setupTabPaneSet('" + paneSet.mSetId + "', '" + paneSet.mSelectedTabId + "');");
}
return buffer.toString();
}
//--------------------------------------------------------------------------
public void addTab(String inLabel, HTMLTag inPaneContent)
{
addTab(inLabel, inPaneContent, false);
}
//--------------------------------------------------------------------------
public void addTab(HTMLTag inLabel, HTMLTag inPaneContent)
{
addTab(inLabel, inPaneContent, false);
}
//--------------------------------------------------------------------------
public void addTab(String inLabel, HTMLTag inPaneContent, boolean inIsSelected)
{
addTab(new Span(inLabel), inPaneContent, inIsSelected);
}
//--------------------------------------------------------------------------
public void addTab(String inLabel, HTMLTag inPaneContent, boolean inIsSelected, String inOnClick)
{
addTab(new Span(inLabel), inPaneContent, inIsSelected, inOnClick);
}
//--------------------------------------------------------------------------
public void addTab(HTMLTag inLabel, HTMLTag inPaneContent, boolean inIsSelected)
{
addTab(new Span(inLabel), inPaneContent, inIsSelected, null);
}
//--------------------------------------------------------------------------
public void addTab(HTMLTag inLabel, HTMLTag inPaneContent, boolean inIsSelected, String inOnClick)
{
sTabCount++;
Td cell = null;
if (horizontal())
{
cell = mLabelTable.getRows().get(0).addCell();
}
else // VERTICAL
{
cell = mLabelTable.addRow().addCell();
}
// Runs thru the switchTab function to get a better error msg if generateOnLoad() wasn't done.
cell.setOnClick((inOnClick != null ? inOnClick + ";" : "")
+ "switchTab('" + mSetId + "', '" + sTabCount + "');");
if (mRoundedTabs)
{
/* Wanted to use hr but stoopid IE gives them some crazy unalterable vertical margins.
cell.addSubtag(new Hr().setClass("u1"));
cell.addSubtag(new Hr().setClass("u2"));
cell.addSubtag(new Hr().setClass("u3"));
cell.addSubtag(new Hr().setClass("u4"));
*/
// For some reason the empty tag renders differently (wrong) compared to the empty tag pair
cell.addDiv().setClass("rndU1 " + (horizontal() ? "rndHzU1" : "rndVertU1")).setContent("");
cell.addDiv().setClass("rndU2 " + (horizontal() ? "rndHzU2" : "rndVertU2")).setContent("");
cell.addDiv().setClass("rndU3 " + (horizontal() ? "rndHzU3" : "rndVertU3")).setContent("");
cell.addDiv().setClass("rndU4 " + (horizontal() ? "rndHzU4" : "rndVertU4")).setContent("");
}
Span tab = cell.addSpan(inLabel);
String tabId = "tab" + sTabCount;
tab.setId(tabId);
tab.setClass(horizontal() ? HORIZ_TAB : VERT_TAB);
if (mRoundedTabs
&& !horizontal())
{
// For some reason the empty tag renders differently (wrong) compared to the empty tag pair
cell.addDiv().setClass("rndU4 rndVertU4").setContent("");
cell.addDiv().setClass("rndU3 rndVertU3").setContent("");
cell.addDiv().setClass("rndU2 rndVertU2").setContent("");
cell.addDiv().setClass("rndU1 rndVertU1").setStyle("border-bottom:1px solid #" + ColorUtil.colorToHex(sDefaultSelectedTabColor)).setContent("");
}
mPaneDiv.addDiv(inPaneContent).setId("pane" + sTabCount);
if (null == mSelectedTabId || inIsSelected) mSelectedTabId = tabId;
}
//##########################################################################
// PRIVATE METHODS
//##########################################################################
//--------------------------------------------------------------------------
private boolean horizontal()
{
return mOrientation == Orientation.HORIZONAL;
}
//--------------------------------------------------------------------------
private void findNestedTabPaneSets(XMLTag inCurrentTag, List inTabPaneSetList)
{
// Find any nested tabpane sets
Iterator iter = inCurrentTag.getSubtags().iterator();
while (iter.hasNext())
{
XMLTag tag = (XMLTag) iter.next();
if (tag instanceof TabbedPaneSet)
{
inTabPaneSetList.add(tag);
}
findNestedTabPaneSets(tag, inTabPaneSetList);
}
}
}