diff --git a/libsrc/tablelayout/build.xml b/libsrc/tablelayout/build.xml
new file mode 100644
index 000000000..ae1a49d7b
--- /dev/null
+++ b/libsrc/tablelayout/build.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project tablelayout.
+
+
+
diff --git a/libsrc/tablelayout/nbproject/build-impl.xml b/libsrc/tablelayout/nbproject/build-impl.xml
new file mode 100644
index 000000000..bc8c6ee04
--- /dev/null
+++ b/libsrc/tablelayout/nbproject/build-impl.xml
@@ -0,0 +1,1413 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.dir
+ Must set test.src.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No tests executed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set JVM to use for profiling in profiler.info.jvm
+ Must set profiler agent JVM arguments in profiler.info.jvmargs.agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set profile.class
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+ This target only works when run from inside the NetBeans IDE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+ Must select some files in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+ Must select one file in the IDE or set test.class
+ Must select some method in the IDE or set test.method
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libsrc/tablelayout/nbproject/genfiles.properties b/libsrc/tablelayout/nbproject/genfiles.properties
new file mode 100644
index 000000000..72e23fc4c
--- /dev/null
+++ b/libsrc/tablelayout/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=e5124a5d
+build.xml.script.CRC32=4efcaf5c
+build.xml.stylesheet.CRC32=8064a381@1.74.2.48
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=e5124a5d
+nbproject/build-impl.xml.script.CRC32=1a6f89d0
+nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.2.48
diff --git a/libsrc/tablelayout/nbproject/project.properties b/libsrc/tablelayout/nbproject/project.properties
new file mode 100644
index 000000000..4a19a9a31
--- /dev/null
+++ b/libsrc/tablelayout/nbproject/project.properties
@@ -0,0 +1,73 @@
+annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=tablelayout
+application.vendor=Jindra
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# Files in build.classes.dir which should be excluded from distribution jar
+dist.archive.excludes=
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=../../lib/tablelayout.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+includes=**
+jar.compress=false
+javac.classpath=
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.7
+javac.target=1.7
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+javac.test.processorpath=\
+ ${javac.test.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=true
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project.
+# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
+# To set system properties for unit tests define test-sys-prop.name=value:
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=src
+test.src.dir=test
diff --git a/libsrc/tablelayout/nbproject/project.xml b/libsrc/tablelayout/nbproject/project.xml
new file mode 100644
index 000000000..4aca3d912
--- /dev/null
+++ b/libsrc/tablelayout/nbproject/project.xml
@@ -0,0 +1,15 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+ tablelayout
+
+
+
+
+
+
+
+
+
diff --git a/libsrc/tablelayout/src/org/xito/dialog/LayoutParser.java b/libsrc/tablelayout/src/org/xito/dialog/LayoutParser.java
new file mode 100644
index 000000000..1c93a5cf5
--- /dev/null
+++ b/libsrc/tablelayout/src/org/xito/dialog/LayoutParser.java
@@ -0,0 +1,464 @@
+// Copyright 2007 Xito.org
+//
+// 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 org.xito.dialog;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.text.*;
+import java.util.Locale;
+
+import javax.xml.parsers.*;
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+/**
+ * The Layout Parser will parse well-formed HTML for the first Table declaration and generate a TableLayout
+ * based on the HTML described Table
+ *
+ * @author Deane Richan
+ */
+public class LayoutParser {
+
+ private static final String TABLE_TAG = "table";
+ private static final String TR_TAG = "tr";
+ private static final String TD_TAG = "td";
+ private static final String WIDTH_ATTR = "width";
+ private static final String MIN_WIDTH_ATTR = "min-width";
+ private static final String MAX_WIDTH_ATTR = "max-width";
+ private static final String HEIGHT_ATTR = "height";
+ private static final String MIN_HEIGHT_ATTR = "min-height";
+ private static final String MAX_HEIGHT_ATTR = "max-height";
+ private static final String ANCHOR_ATTR = "anchor";
+ private static final String CELL_SPACING_ATTR = "cellspacing";
+ private static final String CELL_PADDING_ATTR = "cellpadding";
+ private static final String ALIGN_ATTR = "align";
+ private static final String VALIGN_ATTR = "valign";
+ private static final String COLSPAN_ATTR = "colspan";
+ private static final String ROWSPAN_ATTR = "rowspan";
+ private static final String PADDING_ATTR = "padding";
+ private static final String ID_ATTR = "id";
+ private static final String PREFERRED = "preferred";
+ private static final String LEFT = "left";
+ private static final String RIGHT = "right";
+ private static final String TOP = "top";
+ private static final String BOTTOM = "bottom";
+ private static final String CENTER = "center";
+ private static final String MIDDLE = "middle";
+ private static final String FULL = "full";
+ private static final String NW = "nw";
+ private static final String N = "n";
+ private static final String NE = "ne";
+ private static final String E = "e";
+ private static final String SE = "se";
+ private static final String S = "s";
+ private static final String SW = "sw";
+ private static final String W = "w";
+
+ //percent values need to be in English style
+ private static final DecimalFormat percentFormat = new DecimalFormat("###.##%", new DecimalFormatSymbols(Locale.ENGLISH));
+
+
+ /** Creates a new instance of LayoutParser */
+ public LayoutParser() {
+ }
+
+ public TableLayout parse(String htmlTable) throws IOException {
+
+ return parse(new StringBufferInputStream(htmlTable));
+ }
+
+ public TableLayout parse(URL url) throws IOException {
+
+ try {
+ DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder = fact.newDocumentBuilder();
+ return parse(docBuilder.parse(url.openStream()));
+ } catch (ParserConfigurationException configExp) {
+ configExp.printStackTrace();
+ throw new IOException(configExp.getMessage());
+ } catch (SAXException saxExp) {
+ saxExp.printStackTrace();
+ throw new IOException(saxExp.getMessage());
+ }
+ }
+
+ public TableLayout parse(InputStream in) throws IOException {
+
+ try {
+ DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder = fact.newDocumentBuilder();
+ return parse(docBuilder.parse(in));
+ } catch (ParserConfigurationException configExp) {
+ configExp.printStackTrace();
+ throw new IOException(configExp.getMessage());
+ } catch (SAXException saxExp) {
+ saxExp.printStackTrace();
+ throw new IOException(saxExp.getMessage());
+ }
+ }
+
+ /**
+ * Parse a Document
+ * @param doc
+ * @return
+ * @throws java.io.IOException
+ */
+ public TableLayout parse(Document doc) throws IOException {
+
+ return parse(doc.getDocumentElement());
+ }
+
+ /**
+ * Parse an Element
+ * @param element
+ * @return
+ * @throws java.io.IOException
+ */
+ public TableLayout parse(Element element) throws IOException {
+
+ if (element.getNodeName().equalsIgnoreCase(TABLE_TAG)) {
+ return processTableElement(element);
+ }
+ else {
+ NodeList children = element.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
+ TableLayout layout = parse((Element) children.item(i));
+ if (layout != null) {
+ return layout;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Process the Table Element
+ * @param element
+ * @return
+ * @throws java.io.IOException
+ */
+ private TableLayout processTableElement(Element element) throws IOException {
+
+ TableLayout layout = new TableLayout();
+
+ //Get Width and Height of Table
+ float width = getFloat(element.getAttribute(WIDTH_ATTR));
+ float height = getFloat(element.getAttribute(HEIGHT_ATTR));
+
+ //default to 100% relative
+ if (width == 0) {
+ width = 1.0f;
+ }
+ if (height == 0) {
+ height = 1.0f;
+ }
+ layout.setWidth(width);
+ layout.setHeight(height);
+
+ //get min, max width and height of Table
+ int min_width = getInteger(element.getAttribute(MIN_WIDTH_ATTR), 0);
+ int max_width = getInteger(element.getAttribute(MAX_WIDTH_ATTR), Integer.MAX_VALUE);
+ int min_height = getInteger(element.getAttribute(MIN_HEIGHT_ATTR), 0);
+ int max_height = getInteger(element.getAttribute(MAX_HEIGHT_ATTR), Integer.MAX_VALUE);
+ layout.setMinWidth(min_width);
+ layout.setMaxWidth(max_width);
+ layout.setMinHeight(min_height);
+ layout.setMaxHeight(max_height);
+
+ //Anchor
+ layout.setAnchor(processAnchor(element.getAttribute(ANCHOR_ATTR)));
+
+ //Cell Spacing and Padding
+ int cs = getInteger(element.getAttribute(CELL_SPACING_ATTR), 0);
+ int cp = getInteger(element.getAttribute(CELL_PADDING_ATTR), 0);
+ int padding = cs + cp;
+ layout.setPadding(new Insets(padding, padding, padding, padding));
+
+ //Process Rows
+ NodeList possibleRows = element.getChildNodes();
+ for (int r = 0; r < possibleRows.getLength(); r++) {
+ Node n = possibleRows.item(r);
+ if (n.getNodeName().equalsIgnoreCase(TR_TAG) && n.getNodeType() == Node.ELEMENT_NODE) {
+ Element rowElement = (Element) n;
+ String hStr = rowElement.getAttribute(HEIGHT_ATTR);
+ TableLayout.Row row = new TableLayout.Row(getFloatDimensionValue(hStr));
+ processRow(row, rowElement);
+ layout.addRow(row);
+ }
+ }
+
+ //Add additional RowSpan columns.
+ processRowSpan(layout);
+
+ return layout;
+ }
+
+ /**
+ * When HTML uses RowSpan it automatically inserts extra columns where the
+ * row is spanning over so we need to insert these extra empty columns into the layout
+ * @param layout
+ */
+ private void processRowSpan(TableLayout layout) {
+
+
+ for (int r = 0; r < layout.getRowCount(); r++) {
+ TableLayout.Row row = layout.getRow(r);
+
+ //check for any row spans in the columns
+ for (int c = 0; c < row.getColumnCount(); c++) {
+ TableLayout.Column col = row.getColumn(c);
+
+ if (col.rowSpan > 1) {
+ int span = col.rowSpan - 1;
+ //loop through this many rows below and insert
+ //extra columns
+ for (int s = 1; s <= span; s++) {
+ TableLayout.Row spanRow = layout.getRow(r + s);
+ if (spanRow == null) {
+ continue;
+ }
+ spanRow.insertEmptyColumn(c);
+ }
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * Process a Row
+ * @param row
+ * @param rowElement
+ */
+ private void processRow(TableLayout.Row row, Element rowElement) {
+ NodeList possibleColumns = rowElement.getChildNodes();
+ for (int c = 0; c < possibleColumns.getLength(); c++) {
+ Node n = possibleColumns.item(c);
+ if (n.getNodeName().equalsIgnoreCase(TD_TAG) && n.getNodeType() == Node.ELEMENT_NODE) {
+ Element td = (Element) n;
+ row.addCol(processCol(td));
+ }
+ }
+ }
+
+ /**
+ * Process a Column
+ * @param colElement
+ * @return
+ */
+ private TableLayout.Column processCol(Element colElement) {
+
+ TableLayout.Column col = new TableLayout.Column();
+ String width = colElement.getAttribute(WIDTH_ATTR);
+ String hAlign = colElement.getAttribute(ALIGN_ATTR);
+ String vAlign = colElement.getAttribute(VALIGN_ATTR);
+ String colSpan = colElement.getAttribute(COLSPAN_ATTR);
+ String rowSpan = colElement.getAttribute(ROWSPAN_ATTR);
+
+ col.width = getFloatDimensionValue(width);
+
+ //process col and row spans
+ try {
+ if (colSpan != null && !colSpan.equals("")) {
+ col.colSpan = Integer.parseInt(colSpan);
+ }
+ } catch (NumberFormatException badNum) {
+ System.err.println("Error reading colspan:" + colSpan);
+ }
+
+ try {
+ if (rowSpan != null && !rowSpan.equals("")) {
+ col.rowSpan = Integer.parseInt(rowSpan);
+ }
+ } catch (NumberFormatException badNum) {
+ System.err.println("Error reading rowspan:" + rowSpan);
+ }
+
+ //Horz Align
+ if (hAlign != null && hAlign.equalsIgnoreCase(LEFT)) {
+ col.hAlign = TableLayout.LEFT;
+ }
+ else if (hAlign != null && hAlign.equalsIgnoreCase(RIGHT)) {
+ col.hAlign = TableLayout.RIGHT;
+ }
+ else if (hAlign != null && (hAlign.equalsIgnoreCase(MIDDLE) || hAlign.equalsIgnoreCase(CENTER))) {
+ col.hAlign = TableLayout.CENTER;
+ }
+ else if (hAlign != null && hAlign.equalsIgnoreCase(FULL)) {
+ col.hAlign = TableLayout.FULL;
+ }
+
+ //Vert Align
+ if (vAlign.equals(TOP)) {
+ col.vAlign = TableLayout.TOP;
+ }
+ else if (vAlign.equals(BOTTOM)) {
+ col.vAlign = TableLayout.BOTTOM;
+ }
+ else if (vAlign.equals(CENTER) || vAlign.equals(MIDDLE)) {
+ col.vAlign = TableLayout.CENTER;
+ }
+ else if (vAlign.equals(FULL)) {
+ col.vAlign = TableLayout.FULL;
+ }
+
+ //Process padding
+ col.padding = processColPadding(colElement.getAttribute(PADDING_ATTR));
+
+ //First look for name in ID
+ col.name = colElement.getAttribute(ID_ATTR);
+
+ //Look for name in Text Node if it wasn't in ID
+ if (col.name == null || col.name.length() == 0) {
+ try {
+ NodeList childNodes = colElement.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ if (childNodes.item(i).getNodeType() == Node.TEXT_NODE) {
+ col.name = childNodes.item(i).getNodeValue();
+ break;
+ }
+ }
+ } catch (DOMException badDOM) {
+ System.err.println("Error reading col name");
+ badDOM.printStackTrace();
+ }
+ }
+
+ return col;
+ }
+
+ public Insets processColPadding(String s) {
+ if (s == null || s.equals("")) {
+ return null;
+ }
+ Insets insets = new Insets(0, 0, 0, 0);
+ String values[] = s.split(",");
+ for (int i = 0; i < values.length; i++) {
+ if (i == 0) {
+ insets.top = getInteger(values[i], 0);
+ }
+ if (i == 1) {
+ insets.left = getInteger(values[i], 0);
+ }
+ if (i == 2) {
+ insets.bottom = getInteger(values[i], 0);
+ }
+ if (i == 3) {
+ insets.right = getInteger(values[i], 0);
+ }
+ }
+
+ return insets;
+ }
+
+ /**
+ * Returns the integer value or 0
+ */
+ private int getInteger(String s, int defaultValue) {
+ if (s == null || s.equals("")) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException badNum) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the integer value or 0
+ */
+ private float getFloat(String s) {
+ try {
+ if (s.endsWith("%")) {
+ return percentFormat.parse(s).floatValue();
+ }
+ else {
+ return Float.parseFloat(s);
+ }
+ } catch (ParseException parseExp) {
+ return 0;
+ } catch (NumberFormatException badNum) {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the Anchor int value for the specified String
+ * defaults to NORTH_WEST
+ */
+ public int processAnchor(String s) {
+
+ if (s == null || s.equals("")) {
+ return TableLayout.NORTH_WEST;
+ }
+ if (s.equalsIgnoreCase(NW)) {
+ return TableLayout.NORTH_WEST;
+ }
+ else if (s.equalsIgnoreCase(N)) {
+ return TableLayout.NORTH;
+ }
+ else if (s.equalsIgnoreCase(NE)) {
+ return TableLayout.NORTH_EAST;
+ }
+ else if (s.equalsIgnoreCase(E)) {
+ return TableLayout.EAST;
+ }
+ else if (s.equalsIgnoreCase(SE)) {
+ return TableLayout.SOUTH_EAST;
+ }
+ else if (s.equalsIgnoreCase(S)) {
+ return TableLayout.SOUTH;
+ }
+ else if (s.equalsIgnoreCase(SW)) {
+ return TableLayout.SOUTH_WEST;
+ }
+ else if (s.equalsIgnoreCase(W)) {
+ return TableLayout.WEST;
+ }
+ else if (s.equalsIgnoreCase(CENTER)) {
+ return TableLayout.CENTER;
+ }
+
+ return TableLayout.NORTH_WEST;
+ }
+
+ private float getFloatDimensionValue(String s) {
+ if (s == null || s.equals("") || s.equalsIgnoreCase(PREFERRED)) {
+ return TableLayout.PREFERRED;
+ }
+ if (s.equals("100%")) {
+ s = "99.9999%";
+ }
+ try {
+ if (s.endsWith("%")) {
+ return percentFormat.parse(s).floatValue();
+ }
+ else {
+ return Float.parseFloat(s);
+ }
+ } catch (ParseException parseExp) {
+ System.err.println("Error parsing Dimension value:" + s);
+ return TableLayout.PREFERRED;
+ } catch (NumberFormatException badNum) {
+ System.err.println("Error parsing Dimension value:" + s);
+ return TableLayout.PREFERRED;
+ }
+ }
+}
diff --git a/libsrc/tablelayout/src/org/xito/dialog/TableLayout.java b/libsrc/tablelayout/src/org/xito/dialog/TableLayout.java
new file mode 100644
index 000000000..b57458984
--- /dev/null
+++ b/libsrc/tablelayout/src/org/xito/dialog/TableLayout.java
@@ -0,0 +1,1677 @@
+// Copyright 2007 Xito.org
+//
+// 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 org.xito.dialog;
+
+import java.awt.*;
+import java.net.*;
+import java.util.*;
+import java.io.*;
+import javax.swing.*;
+
+/**
+ *
+ * The TableLayout provides an easy to use layout manager based on the HTML Table. Layouts can be
+ * defined programmatically or by suppling a description in a well formed HTML file. This enables Layouts to be
+ * defined in seperate layout html resource files that can be modified seperately from the codebase.
+ *
+ *
+ * Layouts that are defined in seperate HTML files should contain just the
tags. These files can
+ * be used to preview the layout behavior in your browser. For the most case, table layout behavior, as displayed
+ * in the browser will be duplicate with this layout in your component container.
+ *
+ *
+ * The table layout html should be well formed. The Tidy package is used to tidy any invalidate HTML, and therefore results may very.
+ * It is always best just to use well formed HTML.
+ *
+ * Table Size
+ *
+ * Just as in html if no size of the table is specified as in <table>...</table> the
+ * contents of the container will layout using their preferred sizes.
+ * Generally this will mean components will pack into the North-West corner of the container.
+ *
+ *
+ * If you would like your components to take up the full size of the container use a layout that specifies a percentage
+ * size such as
+ * <table min_width="100%" max_height="100%">...</table>
+ * Of course other percentages will be applied accordingly.
+ *
+ *
+ * If you would like your components to take up a specific min_width and max_height you can specify an absolute size using:
+ * <table min_width="100" max_height="100">...</table>
+ *
+ * Minimum and Maximum Table Sizes
+ *
+ * Although not part of the HTML spec, table layouts can define min-min_width, min-max_height, max-min_width, and max-max_height
+ * attributes. These should not be percent values but instead maximum or minimum size values in pixels.
+ *
+ * Anchor
+ *
+ * Just as HTML table in a web page, TableLayout is anchored to the North-West corner of the parent container.
+ * However an optional anchor attribute can be added to the table element. This attribute will be ignored if min_width
+ * and max_height are set to 100%. The supported values are:
+ * nw, n, ne, v, e, sw, s, se
+ *
+ * Rows
+ *
+ * Rows are defined using the html <tr> element. The max_height attribute has the following behavior:
+ * (undefined) The table row max_height will take the largest preferred max_height of a component in that row.
+ * max_height="50%" the table row max_height will be 50% of the table's max_height.
+ * max_height="100" the table row will be set to 100 pixels high.
+ *
+ * Columns
+ *
+ * Columns are defined using the html <td> element. The following attributes are supported:
+ * id Used to specify an id of this table cell. This can be used in the program to place a component at this id.
+ * min_width Can be undefined, which uses the embedded components preferred min_width, a percentage, or fixed min_width in pixels.
+ * padding Used to specify padding space around the component. Best used with an undefined min_width.
+ * align Used to specify the horizontal alignment of the component in the cell. values are left, center, right, and full.
+ * valign Used to specify the vertical alignment of the component in the cell. values are top, bottom, and full.
+ * colspan Used to specify that this cell's component should span this cell and adjacent cells.
+ * rowspan Used to specify that this cell's component should span this row and adjacent rows.
+ * Note: colspan, and rowspan don't always have the same behavior as these attributes in a browser. These attributes should be used
+ * carefully.
+ *
+ *
+ * Once the Table layout has been processed and added to a container you can use the following to added components to the
+ * container:
+ *
+ * JPanel panel = new JPanel();
+ * panel.setLayout(new TableLayout(html or url));
+ * panel.add("id_1", new JButton()); //where id_1 is the id for the table cell you want to place the component in. The border attribute
+ * is ignored by the TableLayout.
+ *
+ *
+ *
+ * Tip: In order to help test layouts in a browser set the table border="1".
+ *
+ * @author Deane Richan
+ */
+public class TableLayout implements LayoutManager2 {
+
+ public final static int PREFERRED = -1;
+ public final static int LEFT = SwingConstants.LEFT;
+ public final static int RIGHT = SwingConstants.RIGHT;
+ public final static int CENTER = SwingConstants.CENTER;
+ public final static int TOP = SwingConstants.TOP;
+ public final static int BOTTOM = SwingConstants.BOTTOM;
+ public final static int FULL = 999;
+ public final static int NORTH_WEST = SwingConstants.NORTH_WEST;
+ public final static int NORTH = SwingConstants.NORTH;
+ public final static int NORTH_EAST = SwingConstants.NORTH_EAST;
+ public final static int EAST = SwingConstants.EAST;
+ public final static int SOUTH_EAST = SwingConstants.SOUTH_EAST;
+ public final static int SOUTH = SwingConstants.SOUTH;
+ public final static int SOUTH_WEST = SwingConstants.SOUTH_WEST;
+ public final static int WEST = SwingConstants.WEST;
+ public final static float PERCENT_100 = 0.9999f;
+ private ArrayList rows = new ArrayList();
+ private float rowH[];
+ private int rowY[];
+ private int colCount = 0;
+ private float colW[];
+ private int colX[];
+ private int preferredWidth;
+ private int preferredHeight;
+ private int maxCalculatedWidth, maxCalculatedHeight;
+ private float width = 1.0f;
+ private float height = 1.0f;
+ private int anchor = NORTH_WEST;
+ private Insets padding = null;
+ private Dimension lastTargetDim;
+ private int minWidth = 0;
+ private int maxWidth = Integer.MAX_VALUE;
+ private int minHeight = 0;
+ private int maxHeight = Integer.MAX_VALUE;
+ private URL htmlResourceURL;
+
+ public static final String BORDER_LAYOUT = "border_layout";
+ public static final String TITLE_LAYOUT = "title_layout";
+
+ /**
+ * Create a layout for the given resource name. Layouts include:
+ * border_layout
+ * @param name
+ * @return
+ */
+ public static TableLayout createLayout(String name) {
+ return new TableLayout(TableLayout.class.getResource("layouts/" + name + ".html"));
+ }
+
+ /**
+ * Creates a new instance of TableLayout
+ * With min_width of 100%, max_height of 100% and anchor of NORTH_WEST
+ */
+ public TableLayout() {
+ this(PERCENT_100, PERCENT_100, NORTH_WEST);
+ }
+
+ /**
+ * Creates a new instance of TableLayout
+ * @param width either fixed or percentage of container's min_width
+ * @param height either fixed or percentage of container's max_height
+ * @param anchor either NORTH_WEST, NORTH, NORTH_EAST, EAST, SOUTH_EAST, SOUTH, SOUTH_WEST, WEST, or CENTER
+ */
+ public TableLayout(float width, float height, int anchor) {
+ this.width = width;
+ this.height = height;
+ this.anchor = anchor;
+ }
+
+ /** Creates a new instance of TableLayout */
+ public TableLayout(String html) {
+ try {
+ LayoutParser parser = new LayoutParser();
+ TableLayout layout = parser.parse(html);
+ copy(layout);
+ } catch (IOException ioExp) {
+ ioExp.printStackTrace();
+ }
+ }
+
+ /** Creates a new instance of TableLayout */
+ public TableLayout(URL htmlURL) {
+
+ htmlResourceURL = htmlURL;
+
+ try {
+ LayoutParser parser = new LayoutParser();
+ TableLayout layout = parser.parse(htmlURL);
+ copy(layout);
+ } catch (IOException ioExp) {
+ ioExp.printStackTrace();
+ }
+ }
+
+ /** Creates a new instance of TableLayout */
+ public TableLayout(ArrayList rows) {
+ this.rows = rows;
+ if (this.rows == null) {
+ rows = new ArrayList();
+ }
+ }
+
+ /**
+ * Copy settings from a layout to this layout
+ */
+ private void copy(TableLayout layout) {
+
+ this.padding = layout.getPadding();
+ this.width = layout.getWidth();
+ this.minWidth = layout.getMinWidth();
+ this.maxWidth = layout.getMaxWidth();
+ this.height = layout.getHeight();
+ this.minHeight = layout.getMinHeight();
+ this.maxHeight = layout.getMaxHeight();
+
+ this.anchor = layout.anchor;
+
+ //return early if there are no rows in the layout
+ if (layout.rows == null) {
+ return; //copy rows
+ }
+ for (int i = 0; i < layout.rows.size(); i++) {
+ Row r = (Row) ((Row) layout.rows.get(i)).clone();
+ rows.add(r);
+ }
+ }
+
+ public void setWidth(float w) {
+ width = w;
+ }
+
+ public float getWidth() {
+ return width;
+ }
+
+ public void setHeight(float h) {
+ height = h;
+ }
+
+ public float getHeight() {
+ return height;
+ }
+
+ public int getMaxHeight() {
+ return maxHeight;
+ }
+
+ public void setMaxHeight(int maxHeight) {
+ this.maxHeight = maxHeight;
+ }
+
+ public int getMaxWidth() {
+ return maxWidth;
+ }
+
+ public void setMaxWidth(int maxWidth) {
+ this.maxWidth = maxWidth;
+ }
+
+ public int getMinHeight() {
+ return minHeight;
+ }
+
+ public void setMinHeight(int minHeight) {
+ this.minHeight = minHeight;
+ }
+
+ public int getMinWidth() {
+ return minWidth;
+ }
+
+ public void setMinWidth(int minWidth) {
+ this.minWidth = minWidth;
+ }
+
+ public void setAnchor(int a) {
+ anchor = a;
+ }
+
+ public int getAnchor() {
+ return anchor;
+ }
+
+ public void setPadding(Insets p) {
+ padding = p;
+ }
+
+ public Insets getPadding() {
+ return padding;
+ }
+
+ /**
+ * Add a Row to the end of Current Rows
+ */
+ public void addRow(Row r) {
+
+ rows.add(r);
+ lastTargetDim = null;
+ }
+
+ /**
+ * Add a Row
+ */
+ public void addRow(int i, Row r) {
+
+ if (i > rows.size() - 1) {
+ i = rows.size() - 1;
+ }
+ rows.add(i, r);
+ lastTargetDim = null;
+ }
+
+ /**
+ * Remove a Row
+ */
+ public void removeRow(int i) {
+
+ if (i > rows.size() - 1) {
+ return;
+ }
+ rows.remove(i);
+ lastTargetDim = null;
+ }
+
+ /**
+ * Get Row Count
+ */
+ public int getRowCount() {
+
+ return rows.size();
+ }
+
+ /**
+ * Get a Row or null if it doesn't exist
+ */
+ public Row getRow(int r) {
+ try {
+ return (Row) rows.get(r);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Adds the specified component to the layout, using the specified
+ * constraint object.
+ * @param comp the component to be added
+ * @param constraints where/how the component is added to the layout.
+ */
+ @Override
+ public void addLayoutComponent(Component comp, Object constraints) {
+ addLayoutComponent((String) constraints, comp);
+ }
+
+ /**
+ * If the layout manager uses a per-component string,
+ * adds the component comp to the layout,
+ * associating it
+ * with the string specified by name.
+ *
+ * @param name the string to be associated with the component
+ * @param comp the component to be added
+ */
+ @Override
+ public void addLayoutComponent(String name, Component comp) {
+ Column c = this.getColumn(name);
+ if (c != null) {
+ c.component = comp;
+ }
+ }
+
+ /**
+ * Calculates the preferred size dimensions for the specified
+ * container, given the components it contains.
+ * @param target the container to be laid out
+ *
+ * @see #minimumLayoutSize
+ */
+ @Override
+ public Dimension preferredLayoutSize(Container target) {
+
+ if (rows.size() == 0) {
+ return new Dimension(0, 0);
+ }
+
+ Insets is=target.getInsets();
+ calculatePositions(target);
+ return new Dimension(preferredWidth+is.left+is.right, preferredHeight+is.top+is.bottom);
+ }
+
+ /**
+ *
+ * Calculates the minimum size dimensions for the specified
+ * container, given the components it contains.
+ * @param target the component to be laid out
+ * @see #preferredLayoutSize
+ */
+ public Dimension minimumLayoutSize(Container target) {
+
+ int min_height = 0;
+ int min_width = 0;
+ for (int i = 0; i < rows.size(); i++) {
+ Row row = (Row) rows.get(i);
+ float ph = row.getMinimumHeight();
+ if (ph < 1) {
+ min_height = min_height + (int) row.getMinimumHeight();
+ }
+ else {
+ min_height = min_height + (int) ph;
+ }
+
+ float pw = row.getMinimumWidth();
+
+ if (min_width < pw) {
+ min_width = (int) pw;
+ }
+ }
+
+ Insets is=target.getInsets();
+
+ return new Dimension(min_width+is.left+is.right, min_height+is.top+is.bottom);
+ }
+
+ /**
+ *
+ * Calculates the maximum size dimensions for the specified container,
+ * given the components it contains.
+ * @see java.awt.Component#getMaximumSize
+ * @see LayoutManager
+ */
+ @Override
+ public Dimension maximumLayoutSize(Container target) {
+
+ if(rows.size() == 0) {
+ return new Dimension(0,0);
+ }
+
+ calculatePositions(target);
+ Insets is = target.getInsets();
+ return new Dimension(maxCalculatedWidth+is.left+is.right, maxCalculatedHeight+is.top+is.bottom);
+ }
+
+ /**
+ *
+ * Lays out the specified container.
+ * @param parent the container to be laid out
+ */
+ public void layoutContainer(Container parent) {
+
+ if (rows.size() == 0) {
+ return; //Recalculate the Positions
+ }
+ calculatePositions(parent);
+
+ synchronized (parent.getTreeLock()) {
+ Iterator row_it = rows.iterator();
+ int r = 0;
+ while (row_it.hasNext()) {
+ Row row = (Row) row_it.next();
+ if (row.cols != null) {
+
+ Iterator col_it = row.cols.iterator();
+ int c = 0;
+ while (col_it.hasNext()) {
+ layoutColumn(row, (Column)col_it.next(), r, c);
+
+ //iterate col index
+ c++;
+ }
+ }
+
+ //iterate row index
+ r++;
+ }
+
+ }//End of Synchronize Block
+ }
+
+ /**
+ * Layout a Column's component
+ * @param row
+ * @param col
+ * @param r num
+ * @param c num
+ */
+ private void layoutColumn(Row row, Column col, int r, int c) {
+
+ if (col.component == null) {
+ return; //no component so we don't care
+ }
+
+ //get the new x index based on colspan
+ int x = getColXforColSpan(row, c);
+ if(x == -1) {
+ return; //colspan pushed the x past the end of the table
+ }
+
+ //get the w based on colspan
+ int w = getColWidthforColSpan(row, c);
+
+ int y = (int) rowY[r];
+ int h = (int) rowH[r];
+
+ //Compute RowSpan
+ //TODO take this out when new RowSpan is done
+ if (col.rowSpan > 1) {
+ int lastRow = r + (col.rowSpan - 1);
+ if (lastRow >= rowH.length) {
+ lastRow = rowH.length - 1;
+ }
+
+ h = (((int) rowY[lastRow] + (int) rowH[lastRow])) - y;
+ }
+
+ //Compute column Padding
+ if (col.padding != null) {
+ x = x + col.padding.left;
+ y = y + col.padding.top;
+ w = w - col.padding.right;
+ h = h - col.padding.bottom;
+ }
+
+ //TODO this max and min calc has a problem of messing up alignment need to rewrite
+ //Apply Max or Min Width
+ //if(vcol.getMaximumWidth()) v = col.getMaximumWidth();
+
+ //if(hcol.component.getMaximumSize().max_height) h = col.component.getMaximumSize().max_height;
+
+ //Compute Table level Padding
+ if (padding != null) {
+ x = x + padding.left;
+ y = y + padding.top;
+ w = w - padding.right;
+ h = h - padding.bottom;
+ }
+
+ int pw = col.component.getPreferredSize().width;
+ int ph = col.component.getPreferredSize().height;
+
+ //Compute hAlign
+ if (col.hAlign != FULL) {
+
+ if (col.hAlign == RIGHT) {
+ if ((colX[c] + w - pw) >= x) {
+ x = colX[c] + w - pw;
+ }
+ }
+ else if (col.hAlign == CENTER) {
+ int col_center = x + (w / 2);
+ if ((col_center - (pw / 2)) >= x) {
+ x = col_center - (pw / 2);
+ }
+ }
+ if (pw < w) {
+ w = pw;
+ }
+ }
+
+ //Compute vAlign
+ if (col.vAlign != FULL) {
+
+ if (col.vAlign == BOTTOM) {
+ if ((rowY[r] + h - ph) >= y) {
+ y = rowY[r] + h - ph;
+ }
+ }
+ else if (col.vAlign == CENTER) {
+ int row_center = y + (h / 2);
+ if ((row_center - (ph / 2)) >= y) {
+ y = row_center - (ph / 2);
+ }
+ }
+ if (ph < h) {
+ h = ph;
+ }
+ }
+
+ col.component.setLocation(x, y);
+ col.component.setSize(w, h);
+ }
+
+ /**
+ * return -1 if can't determine columns y location
+ * @param r
+ * @param c
+ * @return
+ */
+ private int getRowYforRowSpan(int r, int c) {
+ int rowIndex = getRowIndexforRowSpan(r, c);
+ if(rowIndex > rows.size()-1) {
+ return -1;
+ }
+ else {
+ return rowY[rowIndex];
+ }
+ }
+
+ /**
+ * return -1 if can't determine columns X location
+ * @param row
+ * @param c
+ * @return
+ */
+ private int getColXforColSpan(Row row, int c) {
+ int index = getColIndexforColSpan(row, c);
+ if(index>colX.length-1) {
+ return -1;
+ }
+ else {
+ return (int) colX[index];
+ }
+ }
+
+ /**
+ * Get the column width using colspan
+ * @param row
+ * @param c
+ * @return
+ */
+ private int getColWidthforColSpan(Row row, int c) {
+ int index = getColIndexforColSpan(row, c);
+ int colspan = row.cols.get(c).colSpan;
+
+ float w = 0;
+ for(int i=index;icolW.length-1) {
+ break; //we moved past the end of the table so we just return now
+ }
+ w = w + colW[i];
+ }
+
+ return (int)w;
+ }
+
+ private int getColIndexforColSpan(Row row, int c) {
+ int index = 0;
+ for(int i=0;irow.cols.size()-1) {
+ break;
+ }
+ Column col = row.cols.get(colIndex);
+ index = index + col.rowSpan;
+ }
+
+ return index;
+ }
+
+
+
+ /**
+ * Invalidates the layout, indicating that if the layout manager
+ * has cached information it should be discarded.
+ */
+ public void invalidateLayout(Container target) {
+
+ rowH = null;
+ rowY = null;
+
+ colCount = 0;
+ colW = null;
+ colX = null;
+ }
+
+ /**
+ * Returns the alignment along the y axis. This specifies how
+ * the component would like to be aligned relative to other
+ * components. The value should be a number between 0 and 1
+ * where 0 represents alignment along the origin, 1 is aligned
+ * the furthest away from the origin, 0.5 is centered, etc.
+ */
+ public float getLayoutAlignmentY(Container target) {
+ return 0f;
+ }
+
+ /**
+ * Returns the alignment along the x axis. This specifies how
+ * the component would like to be aligned relative to other
+ * components. The value should be a number between 0 and 1
+ * where 0 represents alignment along the origin, 1 is aligned
+ * the furthest away from the origin, 0.5 is centered, etc.
+ */
+ public float getLayoutAlignmentX(Container target) {
+ return 0f;
+ }
+
+ /**
+ * Removes the specified component from the layout.
+ * @param comp the component to be removed
+ */
+ public void removeLayoutComponent(Component comp) {
+ Iterator it = rows.iterator();
+ while (it.hasNext()) {
+ Row r = (Row) it.next();
+ if (r.cols == null) {
+ continue;
+ }
+ Iterator col_it = r.cols.iterator();
+ while (col_it.hasNext()) {
+ Column c = (Column) col_it.next();
+ if (c.component == comp) {
+ c.component = null;
+ }
+ }
+ }
+
+ lastTargetDim = null;
+ }
+
+ /**
+ * Get a Column by a specified name
+ * The column is a specific Column instance in a specific Row
+ * @return the Column found or null
+ */
+ public Column getColumn(String name) {
+
+ for (int i = 0; i < rows.size(); i++) {
+ Row r = (Row) rows.get(i);
+ Column c = r.getColumn(name);
+ if (c != null) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a Column by row number and col number. 0,0 is the Upper-Left component
+ * @return the Column or null
+ */
+ public Column getColumn(int r, int c) {
+ try {
+ return (Column) ((Row) rows.get(r)).cols.get(c);
+ } catch (IndexOutOfBoundsException badIndex) {
+ return null;
+ } catch (NullPointerException noCols) {
+ return null;
+ }
+ }
+
+ /**
+ * Paint the Layouts Tablelines using the specified graphics context
+ */
+ public void paintTableLines(Container target, Graphics g) {
+
+ if (rowY == null) {
+ calculatePositions(target);
+ }
+
+ Graphics2D g2d = (Graphics2D) g;
+ Dimension size = getSize(target);
+ Point origin = getOrigin(size, target);
+
+ //Draw outside border
+ g2d.setColor(Color.RED);
+ g2d.drawRect(origin.x, origin.y, size.width, size.height);
+
+ //Draw rows
+ for (int i = 0; i < rowY.length; i++) {
+ g2d.drawLine(origin.x, rowY[i], size.width + origin.x, rowY[i]);
+ }
+ //draw columns
+ for (int i = 0; i < colX.length; i++) {
+ int x = colX[i];
+ g2d.drawLine(colX[i], origin.y, colX[i], size.height + origin.y);
+ x = colX[i] + (int) colW[i];
+ g2d.drawLine(x, origin.y, x, size.height + origin.y);
+ }
+
+ }
+
+ /**
+ * Get the Size that this layout wants based on its settings and the container
+ */
+ private Dimension getSize(Container target) {
+ Dimension dim = new Dimension();
+ Insets is=target.getInsets();
+ int twid = target.getWidth() - is.left - is.right;
+ int thei = target.getHeight() - is.top - is.bottom;
+
+ //min_width
+ if (width > 1) {
+ dim.width = (int) width;
+ }
+ else {
+ dim.width = (int) (width * twid);
+ }
+
+ if (dim.width < minWidth) {
+ dim.width = minWidth;
+ }
+ if (dim.width > maxWidth) {
+ dim.width = maxWidth; //max_height
+ }
+ if (height > 1) {
+ dim.height = (int) height;
+ }
+ else {
+ dim.height = (int) (height * thei);
+ }
+
+ if (dim.height < minHeight) {
+ dim.height = minHeight;
+ }
+ if (dim.height > maxHeight) {
+ dim.height = maxHeight;
+ }
+ return dim;
+ }
+
+ private Point getOrigin(Dimension size, Container target) {
+ Point origin = new Point();
+ Insets is=target.getInsets();
+
+ int twid = target.getWidth() - is.left - is.right;
+ int thei = target.getHeight() - is.top - is.bottom;
+
+ origin.x = is.left;
+ origin.y = is.top;
+
+ if (anchor == NORTH_WEST) {
+ origin.x = is.left;
+ origin.y = is.top;
+ }
+ else if (anchor == NORTH) {
+ origin.x = is.left + ((twid - size.width) / 2);
+ origin.y = is.top;
+ }
+ else if (anchor == NORTH_EAST) {
+ origin.x = is.left + twid - size.width;
+ origin.y = is.top;
+ }
+ else if (anchor == EAST) {
+ origin.x = is.left + twid - size.width;
+ origin.y = is.top + ((thei - size.height) / 2);
+ }
+ else if (anchor == SOUTH_EAST) {
+ origin.x = is.left + twid - size.width;
+ origin.y = is.top + thei - size.height;
+ }
+ else if (anchor == SOUTH) {
+ origin.x = is.left + ((twid - size.width) / 2);
+ origin.y = is.top + thei - size.height;
+ }
+ else if (anchor == SOUTH_WEST) {
+ origin.x = is.left;
+ origin.y = is.top + thei - size.height;
+ }
+ else if (anchor == WEST) {
+ origin.x = is.left;
+ origin.y = is.top + (thei - size.height) / 2;
+ }
+ else if (anchor == CENTER) {
+ origin.x = is.left + ((twid - size.width) / 2);
+ origin.y = is.top + (thei- size.height) / 2;
+ }
+
+ return origin;
+ }
+
+ /**
+ * Calculate the Positions of each Cell
+ */
+ public void calculatePositions(Container target) {
+
+ if (rows.size() == 0) {
+ return;
+ }
+
+ //If we already calculated based on this target size then just return
+ if (lastTargetDim != null && target.getWidth() > 0 && target.getHeight() > 0) {
+ if (target.getWidth() == lastTargetDim.width && target.getHeight() == lastTargetDim.height && rowH != null) {
+ return;
+ }
+ }
+
+ Dimension size = getSize(target);
+
+ Point origin = getOrigin(size, target);
+
+ //Calculate Row Heights
+ calculateRowHeightsAndLocations(size.height, origin.y);
+
+ //Calculate Col Widths
+ calculateColWidthsAndLocations(size.width, origin.x);
+
+ lastTargetDim = target.getSize();
+ }
+
+ private boolean isPreferredValue(float v) {
+ return (v == PREFERRED);
+ }
+
+ private boolean isRelativeValue(float v) {
+ return (v > 0 && v < 1.0);
+ }
+
+ private boolean isFixedValue(float v) {
+ return (v >= 1.0);
+ }
+
+ /**
+ * Calculate the row heights and the row locations
+ * @param totalHeight
+ */
+ private void calculateRowHeightsAndLocations(int totalHeight, int y) {
+
+ rowH = new float[rows.size()];
+ rowY = new int[rows.size()];
+
+ //we calculate column count while we are processing rows
+ colCount = 0;
+
+ int[] maxHeights = new int[rows.size()];
+ int[] preferredHeights = new int[rows.size()];
+ for (int r = 0; r < rows.size(); r++) {
+
+ Row row = getRow(r);
+
+ //Get column counts while we are at it
+ if (row.cols != null && row.cols.size() > colCount) {
+ colCount = row.cols.size();
+ }
+
+ //check to see if the row height is fixed or relative
+ if (isFixedValue(row.height)) {
+ maxHeights[r] = (int) row.height;
+ preferredHeights[r] = (int) row.height;
+ rowH[r] = row.height;
+ }
+ else if (isRelativeValue(row.height)) {
+ maxHeights[r] = (int)row.getMaximumHeight();
+ preferredHeights[r] = (int)row.getPreferredHeight();
+ rowH[r] = row.height;
+ }
+ //use the rows preferred height to determine
+ else {
+
+ rowH[r] = row.getPreferredHeight();
+ preferredHeights[r] = (int)rowH[r];
+
+ maxHeights[r] = (int)row.getMaximumHeight();
+
+ /*
+ for (int c = 0; c < row.cols.size(); c++) {
+ Column col = row.getColumn(c);
+ float ph = col.getPreferredHeight();
+ if (ph > rowH[r]) {
+ rowH[r] = ph;
+ }
+
+ //update preferred, max_height
+ if (ph > preferredHeights[r]) {
+ preferredHeights[r] = (int) ph;
+ }
+ }
+ */
+ }
+ }
+
+ preferredHeight = total(preferredHeights);
+ maxCalculatedHeight = total(maxHeights);
+
+ //our preferred Height shouldn't be lower then our min height
+ if(preferredHeight < minHeight) {
+ preferredHeight = minHeight;
+ }
+
+ if(maxCalculatedHeight < maxHeight) {
+ maxCalculatedHeight = maxHeight;
+ }
+
+ //convert relative heights to fixed heights
+ //add up all the fixed heights
+ int fixedHeight = 0;
+ for (int r = 0; r < rowH.length; r++) {
+ if (!isRelativeValue(rowH[r])) {
+ fixedHeight += (int) rowH[r];
+ }
+ }
+
+ //first we fix the percentages to make sure that they add up to 1.0
+ float totalPercentage = 0;
+ for (int r = 0; r < rowH.length; r++) {
+ if (isRelativeValue(rowH[r])) {
+ totalPercentage += rowH[r];
+ }
+ }
+
+ //the fixed relative widths get ratios of the total percentage
+ if (totalPercentage > 1.0) {
+ for (int r = 0; r < rowH.length; r++) {
+ if (isRelativeValue(rowH[r])) {
+ rowH[r] = totalPercentage / rowH[r];
+ }
+ }
+ }
+
+ int remainingHeight = totalHeight - fixedHeight;
+ //convert the relative widths to fixed widths
+ for (int r = 0; r < rowH.length; r++) {
+ if (isRelativeValue(rowH[r])) {
+ int requestedHeight = (int) (remainingHeight * rowH[r]);
+ if (requestedHeight < remainingHeight) {
+ rowH[r] = requestedHeight;
+ remainingHeight -= requestedHeight;
+ }
+ else {
+ rowH[r] = remainingHeight;
+ remainingHeight = 0;
+ }
+ }
+ }
+
+ //Calculate Row Y locations
+ rowY[0] = y;
+ for (int i = 1; i < rowY.length; i++) {
+ y = y + (int) rowH[i - 1];
+ rowY[i] = y;
+ }
+ }
+
+ /**
+ * Get the Column Widths
+ */
+ private void calculateColWidthsAndLocations(int totalWidth, int x) {
+
+ colW = new float[colCount];
+ colX = new int[colCount];
+
+ if (colCount == 0) {
+ return;
+ }
+ if (rows.size() == 0) {
+ return;
+ }
+
+ int[] maxWidths = new int[colW.length];
+ int[] preferredWidths = new int[colW.length];
+ for (int c = 0; c < colW.length; c++) {
+ for (int r = 0; r < rows.size(); r++) {
+ Row row = getRow(r);
+ if (row.cols.size() < (c + 1)) {
+ continue;
+ }
+ Column col = row.getColumn(c);
+ float pw = col.getPreferredWidth();
+
+ int mw = col.getMaximumWidth();
+ if(maxWidths[c] 1.0) {
+ for (int c = 0; c < colW.length; c++) {
+ if (isRelativeValue(colW[c])) {
+ colW[c] = 1 / (totalPercentage / colW[c]);
+ }
+ }
+ }
+
+ int remainingWidth = totalWidth - fixedWidth;
+ //convert the relative widths to fixed widths
+ for (int c = 0; c < colW.length; c++) {
+ if (isRelativeValue(colW[c])) {
+ //if relative min_width is .9999 then they really mean 100%
+ int requestedWidth = (int) (remainingWidth * (colW[c] == PERCENT_100 ? 1.0 : colW[c]));
+ if (requestedWidth < remainingWidth) {
+ colW[c] = requestedWidth;
+ }
+ else {
+ colW[c] = remainingWidth;
+ remainingWidth = 0;
+ }
+ }
+ }
+
+ //Calculate Col X locations
+ colX[0] = x;
+ for (int i = 1; i < colX.length; i++) {
+ x = x + (int) colW[i - 1];
+ colX[i] = x;
+ }
+
+ }
+
+ private float total(float[] values) {
+ if (values == null) {
+ return 0;
+ }
+ float totalValue = 0;
+
+ for (int i = 0; i < values.length; i++) {
+ totalValue += values[i];
+ }
+
+ return totalValue;
+ }
+
+ private int total(int[] values) {
+ if (values == null) {
+ return 0;
+ }
+ int totalValue = 0;
+
+ for (int i = 0; i < values.length; i++) {
+ totalValue += values[i];
+ }
+
+ return totalValue;
+ }
+
+ /**
+ * Get the Column Widths
+ */
+ private void calculateColWidthsOLD(int totalWidth, int colCount) {
+
+ colW = new float[colCount];
+
+ //Get fixed and preferred Widths
+ for (int r = 0; r < rows.size(); r++) {
+ Row row = (Row) rows.get(r);
+ row.updateColWidth(colW);
+ }
+
+ //calculate fixed Height
+ int fixedWidth = 0;
+ for (int i = 0; i < colCount; i++) {
+ if (colW[i] >= 1) {
+ fixedWidth = fixedWidth + (int) colW[i];
+ }
+ }
+
+ //calculate relative Widths
+ int remainingW = totalWidth - fixedWidth;
+ for (int i = 0; i < colCount; i++) {
+ if (remainingW == 0) {
+ continue;
+ }
+ else if (colW[i] > 0 && colW[i] < 1 && remainingW > 0) {
+ //calc relative min_width
+ int w = 0;
+ if (colW[i] > PERCENT_100) {
+ w = (int) (totalWidth - fixedWidth);
+ }
+ else {
+ w = (int) (colW[i] * (totalWidth - fixedWidth));
+ }
+
+ if (w > remainingW) {
+ w = remainingW;
+ }
+ remainingW = remainingW - w;
+ colW[i] = w;
+ }
+ }
+ }
+
+ /*******************************************************************
+ * ROW Class represents Table elements
+ *******************************************************************/
+ public static class Row implements Cloneable {
+
+ public float height = PREFERRED;
+ private ArrayList cols = new ArrayList();
+
+ /**
+ * Create a row with PREFERRED Height
+ */
+ public Row() {
+ height = PREFERRED;
+ }
+
+ /**
+ * Create a row with a specified max_height
+ */
+ public Row(float h) {
+ height = h;
+ }
+
+ public int getColumnCount() {
+ return cols.size();
+ }
+
+ /**
+ * Add a Column to this Row
+ */
+ public void addCol(Column c) {
+ cols.add(c);
+ }
+
+ /**
+ * insert empty column at index. Columns will be added to fill into i
+ * @param index
+ */
+ public void insertEmptyColumn(int index) {
+ if (index > cols.size()) {
+ int count = index - cols.size();
+ for (int i = 0; i < count + 1; i++) {
+ cols.add(new Column());
+ }
+ }
+ else {
+ cols.add(index, new Column());
+ }
+ }
+
+ /**
+ * Copy this row
+ * @return a copy of this row including a copy of all columns
+ */
+ @Override
+ public Object clone() {
+
+ Row rowCopy = new Row();
+ rowCopy.height = this.height;
+ if (this.cols != null) {
+ for (int i = 0; i < this.cols.size(); i++) {
+ Column colCopy = (Column) ((Column) this.cols.get(i)).clone();
+ rowCopy.addCol(colCopy);
+ }
+ }
+
+ return rowCopy;
+ }
+
+ /**
+ * Returns a Rows Preferred Height. Based on the content components of the row
+ */
+ public int getPreferredHeight() {
+
+ //Must be Preferred Height so check the components
+ Iterator it = cols.iterator();
+ int h = 0;
+ while (it.hasNext()) {
+ Column col = it.next();
+ Component comp = col.component;
+ if (comp != null) {
+ int ph = comp.getPreferredSize().height;
+ if (col.padding != null) {
+ ph = ph + col.padding.top + col.padding.bottom;
+ }
+ if (ph > h) {
+ h = ph;
+ }
+ }
+ }
+
+ return h;
+ }
+
+ /**
+ * Returns a Rows Maximum Height based on calculating the maximum components height in this row
+ */
+ public int getMaximumHeight() {
+
+ Iterator it = cols.iterator();
+ int h = 0;
+ while (it.hasNext()) {
+ Column col = it.next();
+ Component comp = col.component;
+ if (comp != null) {
+ int mh = comp.getMaximumSize().height;
+ if (col.padding != null) {
+ mh = mh + col.padding.top + col.padding.bottom;
+ }
+ if (mh > h) {
+ h = mh;
+ }
+ }
+ }
+
+ return h;
+ }
+
+ /**
+ * Returns a Rows Maximum Width. If row is PREFERRED then returns the max Width column components
+ * If row is relative Percentage or fixed then returns 1
+ */
+ public int getMaximumWidth() {
+
+ if (cols == null) {
+ return 0;
+ }
+ Iterator it = cols.iterator();
+ int w = 0;
+ while (it.hasNext()) {
+ Column col = (Column) it.next();
+ w = w + col.getMaximumWidth();
+ }
+
+ return w;
+ }
+
+ /**
+ * Returns a Rows Minimum Width. If row is PREFERRED then returns the min Width column components
+ * If row is relative Percentage or fixed then returns 1
+ */
+ public int getMinimumWidth() {
+
+ if (cols == null) {
+ return 0;
+ }
+ Iterator it = cols.iterator();
+ int w = 0;
+ while (it.hasNext()) {
+ Column col = (Column) it.next();
+ float pw = col.getMinimumWidth();
+
+ if (pw > w) {
+ w = (int) pw;
+ }
+ }
+
+ return w;
+ }
+
+ /**
+ * Returns a Rows Preferred Width by getting the sum of all columns widths.
+ */
+ public int getPreferredWidth() {
+
+ if (cols == null) {
+ return 0;
+ }
+ Iterator it = cols.iterator();
+ int w = 0;
+ while (it.hasNext()) {
+ Column col = (Column) it.next();
+ float pw = col.getPreferredWidth();
+ if (pw < 1) {
+ pw = col.getMinimumWidth();
+ }
+
+ w = w + (int) pw;
+
+ }
+
+ return w;
+ }
+
+ /**
+ * Returns a Rows Minimum Height.
+ * IF row is fixed then returns the fixed row max_height
+ * If row is PREFERRED or relative Percentage
+ * then returns the max minimum Height of all column components
+ */
+ public int getMinimumHeight() {
+
+ //If a fixed max_height
+ if (height >= 1) {
+ return (int) height; //must be preferred or relative in which case we just return the min max_height
+ }
+ if (cols == null) {
+ return 0;
+ }
+ Iterator it = cols.iterator();
+ int h = 0;
+ while (it.hasNext()) {
+ Column col = (Column) it.next();
+ Component comp = col.component;
+ if (comp != null) {
+ int mh = comp.getMinimumSize().height;
+ if (col.padding != null) {
+ mh = mh + col.padding.top + col.padding.bottom;
+ }
+ if (mh > h) {
+ h = mh;
+ }
+ }
+ }
+
+ return h;
+ }
+
+ /**
+ * Calculate the Column Widths that this Row wants. Existing column widths are passed in and
+ * if this row's columns want widths that are larger then it replaces just those widths with its
+ * own columns widths
+ */
+ protected float[] updateColWidth(float colW[]) {
+
+ //If we don't have any columns then we can't figure it out
+ //so just return what we got
+ if (cols == null) {
+ return colW; //Walk through each col
+ }
+ float relativeWidth = 0;
+ for (int i = 0; i < colW.length; i++) {
+ try {
+ Column col = (Column) cols.get(i);
+
+ //The preferred min_width is either a fixed min_width or a relative min_width
+ float pw = col.getPreferredWidth();
+
+ //If a percentage min_width then make sure we have room for it
+ if (pw > 0 && pw < 1) {
+ if ((relativeWidth + pw) > 1) {
+ pw = col.getMinimumWidth();
+ relativeWidth = 1.0f;
+ }
+ else {
+ relativeWidth = relativeWidth + pw;
+ }
+ }
+
+ //If fixed min_width was based on preferred component min_width then
+ //use it if its greater then what we have and what we have is not a relative min_width
+ if ((col.width == PREFERRED) && (pw > colW[i]) && (colW[i] >= 1)) {
+ colW[i] = pw;
+ } //Else use the fixed or percentage min_width if we don't have a setting yet
+ else if (colW[i] == 0) {
+ colW[i] = pw;
+ }
+ } //Cols that we don't have just get skipped
+ catch (IndexOutOfBoundsException noCol) {
+ }
+ }
+
+ return colW;
+ }
+
+ /**
+ * Get a Column for a specific Name
+ */
+ public Column getColumn(String name) {
+ if (cols == null) {
+ return null;
+ }
+ Iterator it = cols.iterator();
+ while (it.hasNext()) {
+ Column c = (Column) it.next();
+ if (c.name != null && c.name.equals(name)) {
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ public Column getColumn(int index) {
+ if (index >= cols.size()) {
+ return null;
+ }
+ return (Column) cols.get(index);
+ }
+ }
+
+ /*******************************************************************
+ * Column Class represents Table | elements or Columns in Rows
+ *******************************************************************/
+ public static class Column implements Cloneable {
+
+ public float width = PREFERRED;
+ public String name;
+ public Component component;
+ public int colSpan = 1;
+ public int rowSpan = 1;
+ public int hAlign = LEFT;
+ public int vAlign = CENTER;
+ Insets padding = null;
+
+ /**
+ * Create a Column
+ */
+ public Column() {
+ }
+
+ /**
+ * Create a Column with a Name
+ */
+ public Column(String n) {
+ name = n;
+ }
+
+ /**
+ * Create a Column with a Width
+ */
+ public Column(float w) {
+ width = w;
+ }
+
+ /**
+ * Create a Column with name and min_width
+ */
+ public Column(String n, float w) {
+ name = n;
+ width = w;
+ }
+
+ /**
+ * Create a Column with a specific column and row span
+ */
+ public Column(String n, float w, int cspan, int rspan) {
+ name = n;
+ width = w;
+ colSpan = cspan;
+ rowSpan = rspan;
+ }
+
+ /**
+ * Get a copy of this column information
+ * @return
+ */
+ @Override
+ public Object clone() {
+ Column colCopy = new Column();
+
+ colCopy.width = this.width;
+ colCopy.name = this.name;
+ colCopy.component = null; //we are copying the layout not the components in it
+ colCopy.colSpan = this.colSpan;
+ colCopy.rowSpan = this.rowSpan;
+ colCopy.hAlign = this.hAlign;
+ colCopy.vAlign = this.vAlign;
+ if (this.padding != null) {
+ colCopy.padding = (Insets) this.padding.clone();
+ }
+
+ return colCopy;
+ }
+
+ /**
+ * Returns a Columns Preferred Width. If the column is PREFERRED then returns this column's component preferred min_width
+ * If the column is a relative percentage then returns that percentage
+ */
+ public float getPreferredWidth() {
+ float pw = 0;
+ if (width == PREFERRED && component != null && colSpan <= 1) {
+ pw = component.getPreferredSize().width;
+ return pw;
+ }
+ else if (width == PREFERRED && (component == null || colSpan > 1)) {
+ return 0;
+ }
+ else if (width >= 1 || width == 0) {
+
+ pw = width;
+ if (padding != null) {
+ pw = pw + padding.left + padding.right;
+ }
+
+ return pw;
+ }
+ else {
+ return width;
+ }
+ }
+
+ public int getPreferredHeight() {
+ if (component == null) {
+ return 0;
+ }
+ else {
+ return component.getPreferredSize().height;
+ }
+ }
+
+ public int getComponentPreferredWidth() {
+ if (component == null) {
+ return 0;
+ }
+ else {
+ return component.getPreferredSize().width;
+ }
+ }
+
+ /**
+ * Gets this Columns Components Minimum Width
+ */
+ public int getMinimumWidth() {
+
+ //if its fixed then just return it
+ if (width > 1) {
+ return (int) width; //if no component then just 0
+ }
+ if (component == null) {
+ return 0;
+ }
+ return component.getMinimumSize().width;
+ }
+
+ /**
+ * Gets this Columns Components Maximum Width
+ */
+ public int getMaximumWidth() {
+
+ //if its fixed then just return it
+ if (width > 1) {
+ return (int) width; //if no component then just 0
+ }
+ if (component == null) {
+ return 0;
+ }
+ return component.getMaximumSize().width;
+ }
+ }
+}
+
|