[opticalraytracer] 01/06: * Update to upstream 9.0

D Haley mycae-guest at moszumanska.debian.org
Thu May 14 22:55:08 UTC 2015


This is an automated email from the git hooks/post-receive script.

mycae-guest pushed a commit to branch master
in repository opticalraytracer.

commit 2bc622d7d54d5bb9bc5657c99cd4bc8211bec233
Author: D Haley <mycae at gmx.com>
Date:   Fri May 15 00:14:46 2015 +0200

    * Update to upstream 9.0
---
 .classpath                                         |    6 +
 .project                                           |   17 +
 .settings/org.eclipse.jdt.core.prefs               |   11 +
 MANIFEST.mf                                        |    2 +
 libraries/plugin.jar                               |  Bin 959658 -> 0 bytes
 .../plugin/netscape/javascript/JSException.class   |  Bin 1455 -> 0 bytes
 .../plugin/netscape/javascript/JSObject.class      |  Bin 1220 -> 0 bytes
 libraries/plugin/netscape/javascript/JSUtil.class  |  Bin 596 -> 0 bytes
 .../security/ForbiddenTargetException.class        |  Bin 336 -> 0 bytes
 .../netscape/security/ParameterizedTarget.class    |  Bin 1120 -> 0 bytes
 libraries/plugin/netscape/security/Principal.class |  Bin 1644 -> 0 bytes
 libraries/plugin/netscape/security/Privilege.class |  Bin 1752 -> 0 bytes
 .../netscape/security/PrivilegeManager.class       |  Bin 3331 -> 0 bytes
 .../plugin/netscape/security/PrivilegeTable.class  |  Bin 1458 -> 0 bytes
 libraries/plugin/netscape/security/Target.class    |  Bin 2946 -> 0 bytes
 .../netscape/security/UserDialogHelper.class       |  Bin 701 -> 0 bytes
 .../plugin/netscape/security/UserTarget.class      |  Bin 995 -> 0 bytes
 .../sun/plugin/AppletObjectInputStream.class       |  Bin 1990 -> 0 bytes
 .../plugin/sun/plugin/AppletStatusListener.class   |  Bin 163 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer$1.class   |  Bin 808 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer$2.class   |  Bin 675 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer$3.class   |  Bin 783 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer$4.class   |  Bin 537 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer$5.class   |  Bin 616 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer$6.class   |  Bin 616 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer$7.class   |  Bin 677 -> 0 bytes
 .../plugin/AppletViewer$AppletEventListener.class  |  Bin 923 -> 0 bytes
 .../sun/plugin/AppletViewer$GrayBoxListener.class  |  Bin 2668 -> 0 bytes
 libraries/plugin/sun/plugin/AppletViewer.class     |  Bin 36190 -> 0 bytes
 libraries/plugin/sun/plugin/BeansApplet.class      |  Bin 932 -> 0 bytes
 libraries/plugin/sun/plugin/BeansViewer.class      |  Bin 3403 -> 0 bytes
 .../plugin/sun/plugin/ClassLoaderInfo$1.class      |  Bin 720 -> 0 bytes
 .../plugin/ClassLoaderInfo$LoaderReference.class   |  Bin 885 -> 0 bytes
 libraries/plugin/sun/plugin/ClassLoaderInfo.class  |  Bin 7839 -> 0 bytes
 libraries/plugin/sun/plugin/JavaRunTime$1.class    |  Bin 596 -> 0 bytes
 libraries/plugin/sun/plugin/JavaRunTime$2.class    |  Bin 601 -> 0 bytes
 libraries/plugin/sun/plugin/JavaRunTime.class      |  Bin 6965 -> 0 bytes
 .../sun/plugin/PluginURLJarFileCallBack$1.class    |  Bin 3870 -> 0 bytes
 .../sun/plugin/PluginURLJarFileCallBack.class      |  Bin 1899 -> 0 bytes
 libraries/plugin/sun/plugin/WJcovUtil.class        |  Bin 232 -> 0 bytes
 .../sun/plugin/cache/CacheUpdateHelper.class       |  Bin 2295 -> 0 bytes
 .../plugin/sun/plugin/cache/FileVersion.class      |  Bin 2679 -> 0 bytes
 .../plugin/sun/plugin/cache/JarCacheUtil.class     |  Bin 3018 -> 0 bytes
 .../plugin/cache/JarCacheVersionException.class    |  Bin 328 -> 0 bytes
 .../plugin/sun/plugin/cache/OldCacheEntry$1.class  |  Bin 652 -> 0 bytes
 .../plugin/sun/plugin/cache/OldCacheEntry.class    |  Bin 3919 -> 0 bytes
 libraries/plugin/sun/plugin/dom/DOMAccessor.class  |  Bin 807 -> 0 bytes
 .../plugin/sun/plugin/dom/DOMImplementation.class  |  Bin 1899 -> 0 bytes
 libraries/plugin/sun/plugin/dom/DOMObject.class    |  Bin 2389 -> 0 bytes
 .../plugin/sun/plugin/dom/DOMObjectFactory.class   |  Bin 8590 -> 0 bytes
 .../plugin/sun/plugin/dom/DOMObjectHelper.class    |  Bin 2537 -> 0 bytes
 .../sun/plugin/dom/DOMObjectTypeHelper.class       |  Bin 2106 -> 0 bytes
 libraries/plugin/sun/plugin/dom/DOMService.class   |  Bin 698 -> 0 bytes
 .../plugin/sun/plugin/dom/DOMServiceProvider.class |  Bin 1459 -> 0 bytes
 libraries/plugin/sun/plugin/dom/core/Attr.class    |  Bin 3060 -> 0 bytes
 .../plugin/sun/plugin/dom/core/CharacterData.class |  Bin 2490 -> 0 bytes
 libraries/plugin/sun/plugin/dom/core/Comment.class |  Bin 524 -> 0 bytes
 .../plugin/sun/plugin/dom/core/Document.class      |  Bin 5717 -> 0 bytes
 .../sun/plugin/dom/core/DocumentFragment.class     |  Bin 941 -> 0 bytes
 libraries/plugin/sun/plugin/dom/core/Element.class |  Bin 4054 -> 0 bytes
 libraries/plugin/sun/plugin/dom/core/Node.class    |  Bin 7762 -> 0 bytes
 libraries/plugin/sun/plugin/dom/core/Text.class    |  Bin 1525 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSCharsetRule.class |  Bin 981 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSConstants.class   |  Bin 7281 -> 0 bytes
 .../sun/plugin/dom/css/CSSFontFaceRule.class       |  Bin 1182 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSImportRule.class  |  Bin 1545 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSMediaRule.class   |  Bin 2335 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSPageRule.class    |  Bin 1660 -> 0 bytes
 .../sun/plugin/dom/css/CSSPrimitiveValue.class     |  Bin 6229 -> 0 bytes
 libraries/plugin/sun/plugin/dom/css/CSSRule.class  |  Bin 1422 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSRuleList.class    |  Bin 1381 -> 0 bytes
 .../sun/plugin/dom/css/CSSStyleDeclaration.class   |  Bin 21253 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSStyleRule.class   |  Bin 1735 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSStyleSheet.class  |  Bin 2205 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSUnknownRule.class |  Bin 402 -> 0 bytes
 libraries/plugin/sun/plugin/dom/css/CSSValue.class |  Bin 3346 -> 0 bytes
 .../plugin/sun/plugin/dom/css/CSSValueList.class   |  Bin 3502 -> 0 bytes
 libraries/plugin/sun/plugin/dom/css/RGBColor.class |  Bin 2439 -> 0 bytes
 libraries/plugin/sun/plugin/dom/css/Rect.class     |  Bin 2489 -> 0 bytes
 libraries/plugin/sun/plugin/dom/css/ViewCSS.class  |  Bin 574 -> 0 bytes
 .../exception/BrowserNotSupportedException.class   |  Bin 313 -> 0 bytes
 .../dom/exception/HierarchyRequestException.class  |  Bin 306 -> 0 bytes
 .../dom/exception/InvalidAccessException.class     |  Bin 301 -> 0 bytes
 .../dom/exception/InvalidStateException.class      |  Bin 299 -> 0 bytes
 .../exception/NoModificationAllowedException.class |  Bin 317 -> 0 bytes
 .../exception/PluginNotSupportedException.class    |  Bin 311 -> 0 bytes
 .../dom/exception/WrongDocumentException.class     |  Bin 300 -> 0 bytes
 .../sun/plugin/dom/html/HTMLAnchorElement.class    |  Bin 2606 -> 0 bytes
 .../sun/plugin/dom/html/HTMLAppletElement.class    |  Bin 1951 -> 0 bytes
 .../sun/plugin/dom/html/HTMLAreaElement.class      |  Bin 1996 -> 0 bytes
 .../plugin/sun/plugin/dom/html/HTMLBRElement.class |  Bin 644 -> 0 bytes
 .../sun/plugin/dom/html/HTMLBaseElement.class      |  Bin 778 -> 0 bytes
 .../sun/plugin/dom/html/HTMLBaseFontElement.class  |  Bin 912 -> 0 bytes
 .../sun/plugin/dom/html/HTMLBodyElement.class      |  Bin 1305 -> 0 bytes
 .../sun/plugin/dom/html/HTMLButtonElement.class    |  Bin 2228 -> 0 bytes
 .../sun/plugin/dom/html/HTMLCollection.class       |  Bin 1275 -> 0 bytes
 .../plugin/sun/plugin/dom/html/HTMLConstants.class |  Bin 7264 -> 0 bytes
 .../sun/plugin/dom/html/HTMLDListElement.class     |  Bin 738 -> 0 bytes
 .../sun/plugin/dom/html/HTMLDirectoryElement.class |  Bin 750 -> 0 bytes
 .../sun/plugin/dom/html/HTMLDivElement.class       |  Bin 647 -> 0 bytes
 .../plugin/sun/plugin/dom/html/HTMLDocument.class  |  Bin 5634 -> 0 bytes
 .../plugin/sun/plugin/dom/html/HTMLElement.class   |  Bin 1949 -> 0 bytes
 .../sun/plugin/dom/html/HTMLFieldSetElement.class  |  Bin 938 -> 0 bytes
 .../sun/plugin/dom/html/HTMLFontElement.class      |  Bin 900 -> 0 bytes
 .../sun/plugin/dom/html/HTMLFormElement.class      |  Bin 2189 -> 0 bytes
 .../sun/plugin/dom/html/HTMLFrameElement.class     |  Bin 2111 -> 0 bytes
 .../sun/plugin/dom/html/HTMLFrameSetElement.class  |  Bin 784 -> 0 bytes
 .../plugin/sun/plugin/dom/html/HTMLHRElement.class |  Bin 1290 -> 0 bytes
 .../sun/plugin/dom/html/HTMLHeadElement.class      |  Bin 656 -> 0 bytes
 .../sun/plugin/dom/html/HTMLHeadingElement.class   |  Bin 659 -> 0 bytes
 .../sun/plugin/dom/html/HTMLHtmlElement.class      |  Bin 656 -> 0 bytes
 .../sun/plugin/dom/html/HTMLIFrameElement.class    |  Bin 2106 -> 0 bytes
 .../sun/plugin/dom/html/HTMLImageElement.class     |  Bin 2460 -> 0 bytes
 .../sun/plugin/dom/html/HTMLInputElement.class     |  Bin 4066 -> 0 bytes
 .../sun/plugin/dom/html/HTMLIsIndexElement.class   |  Bin 1246 -> 0 bytes
 .../plugin/sun/plugin/dom/html/HTMLLIElement.class |  Bin 1020 -> 0 bytes
 .../sun/plugin/dom/html/HTMLLabelElement.class     |  Bin 1383 -> 0 bytes
 .../sun/plugin/dom/html/HTMLLegendElement.class    |  Bin 1380 -> 0 bytes
 .../sun/plugin/dom/html/HTMLLinkElement.class      |  Bin 2609 -> 0 bytes
 .../sun/plugin/dom/html/HTMLMapElement.class       |  Bin 1224 -> 0 bytes
 .../sun/plugin/dom/html/HTMLMenuElement.class      |  Bin 735 -> 0 bytes
 .../sun/plugin/dom/html/HTMLMetaElement.class      |  Bin 1052 -> 0 bytes
 .../sun/plugin/dom/html/HTMLModElement.class       |  Bin 781 -> 0 bytes
 .../sun/plugin/dom/html/HTMLOListElement.class     |  Bin 1341 -> 0 bytes
 .../sun/plugin/dom/html/HTMLObjectElement.class    |  Bin 3784 -> 0 bytes
 .../sun/plugin/dom/html/HTMLOptGroupElement.class  |  Bin 1058 -> 0 bytes
 .../sun/plugin/dom/html/HTMLOptionElement.class    |  Bin 2242 -> 0 bytes
 .../sun/plugin/dom/html/HTMLParagraphElement.class |  Bin 665 -> 0 bytes
 .../sun/plugin/dom/html/HTMLParamElement.class     |  Bin 1043 -> 0 bytes
 .../sun/plugin/dom/html/HTMLPreElement.class       |  Bin 718 -> 0 bytes
 .../sun/plugin/dom/html/HTMLQuoteElement.class     |  Bin 650 -> 0 bytes
 .../sun/plugin/dom/html/HTMLScriptElement.class    |  Bin 1683 -> 0 bytes
 .../sun/plugin/dom/html/HTMLSelectElement.class    |  Bin 3762 -> 0 bytes
 .../sun/plugin/dom/html/HTMLStyleElement.class     |  Bin 1174 -> 0 bytes
 .../plugin/dom/html/HTMLTableCaptionElement.class  |  Bin 674 -> 0 bytes
 .../sun/plugin/dom/html/HTMLTableCellElement.class |  Bin 2860 -> 0 bytes
 .../sun/plugin/dom/html/HTMLTableColElement.class  |  Bin 1544 -> 0 bytes
 .../sun/plugin/dom/html/HTMLTableElement.class     |  Bin 4934 -> 0 bytes
 .../sun/plugin/dom/html/HTMLTableRowElement.class  |  Bin 2604 -> 0 bytes
 .../plugin/dom/html/HTMLTableSectionElement.class  |  Bin 2195 -> 0 bytes
 .../sun/plugin/dom/html/HTMLTextAreaElement.class  |  Bin 3066 -> 0 bytes
 .../sun/plugin/dom/html/HTMLTitleElement.class     |  Bin 650 -> 0 bytes
 .../sun/plugin/dom/html/HTMLUListElement.class     |  Bin 1043 -> 0 bytes
 .../plugin/dom/html/common/HTMLCollection.class    |  Bin 973 -> 0 bytes
 .../plugin/dom/html/ns4/HTMLAnchorCollection.class |  Bin 855 -> 0 bytes
 .../plugin/dom/html/ns4/HTMLAppletCollection.class |  Bin 855 -> 0 bytes
 .../plugin/dom/html/ns4/HTMLFormCollection.class   |  Bin 851 -> 0 bytes
 .../plugin/dom/html/ns4/HTMLImageCollection.class  |  Bin 853 -> 0 bytes
 .../sun/plugin/dom/html/ns4/NS4DOMObject.class     |  Bin 932 -> 0 bytes
 .../sun/plugin/dom/stylesheets/MediaList.class     |  Bin 1655 -> 0 bytes
 .../sun/plugin/dom/stylesheets/SSLConstants.class  |  Bin 784 -> 0 bytes
 .../sun/plugin/dom/stylesheets/StyleSheet.class    |  Bin 2402 -> 0 bytes
 .../plugin/dom/stylesheets/StyleSheetList.class    |  Bin 1218 -> 0 bytes
 .../plugin/sun/plugin/dom/views/AbstractView.class |  Bin 462 -> 0 bytes
 .../extension/ExtensionInstallationImpl$1.class    |  Bin 3021 -> 0 bytes
 .../extension/ExtensionInstallationImpl.class      |  Bin 9210 -> 0 bytes
 .../sun/plugin/extension/ExtensionInstaller.class  |  Bin 349 -> 0 bytes
 .../sun/plugin/extension/ExtensionUtils$1.class    |  Bin 716 -> 0 bytes
 .../sun/plugin/extension/ExtensionUtils.class      |  Bin 2725 -> 0 bytes
 .../plugin/extension/JavaExtensionInstaller.class  |  Bin 1607 -> 0 bytes
 .../extension/NativeExtensionInstaller.class       |  Bin 3476 -> 0 bytes
 .../plugin/extension/RawExtensionInstaller.class   |  Bin 1466 -> 0 bytes
 .../sun/plugin/javascript/JSClassLoader$1.class    |  Bin 892 -> 0 bytes
 .../sun/plugin/javascript/JSClassLoader.class      |  Bin 5625 -> 0 bytes
 .../plugin/sun/plugin/javascript/JSContext.class   |  Bin 178 -> 0 bytes
 .../plugin/sun/plugin/javascript/JSInvoke.class    |  Bin 564 -> 0 bytes
 .../plugin/sun/plugin/javascript/JSObject.class    |  Bin 1729 -> 0 bytes
 .../plugin/sun/plugin/javascript/ReflectUtil.class |  Bin 3914 -> 0 bytes
 .../sun/plugin/javascript/navig/Anchor.class       |  Bin 259 -> 0 bytes
 .../sun/plugin/javascript/navig/AnchorArray.class  |  Bin 536 -> 0 bytes
 .../plugin/sun/plugin/javascript/navig/Array.class |  Bin 1465 -> 0 bytes
 .../sun/plugin/javascript/navig/Document.class     |  Bin 2970 -> 0 bytes
 .../sun/plugin/javascript/navig/Element.class      |  Bin 1877 -> 0 bytes
 .../sun/plugin/javascript/navig/ElementArray.class |  Bin 693 -> 0 bytes
 .../plugin/sun/plugin/javascript/navig/Form.class  |  Bin 2257 -> 0 bytes
 .../sun/plugin/javascript/navig/FormArray.class    |  Bin 530 -> 0 bytes
 .../sun/plugin/javascript/navig/FrameArray.class   |  Bin 534 -> 0 bytes
 .../sun/plugin/javascript/navig/History.class      |  Bin 925 -> 0 bytes
 .../plugin/sun/plugin/javascript/navig/Image.class |  Bin 899 -> 0 bytes
 .../sun/plugin/javascript/navig/ImageArray.class   |  Bin 533 -> 0 bytes
 .../sun/plugin/javascript/navig/JSObject.class     |  Bin 4098 -> 0 bytes
 .../plugin/javascript/navig/JSObjectFactory.class  |  Bin 3479 -> 0 bytes
 .../plugin/javascript/navig/JSObjectResolver.class |  Bin 341 -> 0 bytes
 .../sun/plugin/javascript/navig/JSType.class       |  Bin 1436 -> 0 bytes
 .../plugin/sun/plugin/javascript/navig/Link.class  |  Bin 654 -> 0 bytes
 .../sun/plugin/javascript/navig/LinkArray.class    |  Bin 530 -> 0 bytes
 .../sun/plugin/javascript/navig/Location.class     |  Bin 693 -> 0 bytes
 .../sun/plugin/javascript/navig/Navigator.class    |  Bin 922 -> 0 bytes
 .../sun/plugin/javascript/navig/Option.class       |  Bin 800 -> 0 bytes
 .../sun/plugin/javascript/navig/OptionArray.class  |  Bin 536 -> 0 bytes
 .../plugin/sun/plugin/javascript/navig/URL.class   |  Bin 851 -> 0 bytes
 .../sun/plugin/javascript/navig/Window.class       |  Bin 4395 -> 0 bytes
 .../sun/plugin/javascript/navig4/Anchor.class      |  Bin 660 -> 0 bytes
 .../sun/plugin/javascript/navig4/Document.class    |  Bin 1355 -> 0 bytes
 .../plugin/javascript/navig4/JSObjectFactory.class |  Bin 2024 -> 0 bytes
 .../sun/plugin/javascript/navig4/Layer.class       |  Bin 2817 -> 0 bytes
 .../sun/plugin/javascript/navig4/LayerArray.class  |  Bin 534 -> 0 bytes
 .../plugin/sun/plugin/javascript/navig4/Link.class |  Bin 654 -> 0 bytes
 .../sun/plugin/javascript/navig4/Navigator.class   |  Bin 782 -> 0 bytes
 .../sun/plugin/javascript/navig4/UIBar.class       |  Bin 663 -> 0 bytes
 .../sun/plugin/javascript/navig4/Window.class      |  Bin 2819 -> 0 bytes
 .../sun/plugin/javascript/navig5/JSObject.class    |  Bin 4234 -> 0 bytes
 .../SecurityContext$PrivilegedBlockAction.class    |  Bin 1323 -> 0 bytes
 .../plugin/javascript/navig5/SecurityContext.class |  Bin 2181 -> 0 bytes
 .../plugin/liveconnect/JavaScriptPermission.class  |  Bin 374 -> 0 bytes
 .../liveconnect/JavaScriptProtectionDomain.class   |  Bin 1052 -> 0 bytes
 .../sun/plugin/liveconnect/LiveConnect.class       |  Bin 247 -> 0 bytes
 .../liveconnect/OriginNotAllowedException.class    |  Bin 336 -> 0 bytes
 .../liveconnect/PrivilegedCallMethodAction.class   |  Bin 2036 -> 0 bytes
 .../PrivilegedConstructObjectAction.class          |  Bin 894 -> 0 bytes
 .../liveconnect/PrivilegedGetFieldAction.class     |  Bin 828 -> 0 bytes
 .../liveconnect/PrivilegedSetFieldAction.class     |  Bin 889 -> 0 bytes
 .../sun/plugin/liveconnect/ReplaceMethod.class     |  Bin 1479 -> 0 bytes
 .../plugin/liveconnect/SecureInvocation$1.class    |  Bin 1916 -> 0 bytes
 .../plugin/liveconnect/SecureInvocation$2.class    |  Bin 1994 -> 0 bytes
 .../plugin/liveconnect/SecureInvocation$3.class    |  Bin 1865 -> 0 bytes
 .../plugin/liveconnect/SecureInvocation$4.class    |  Bin 1946 -> 0 bytes
 .../plugin/liveconnect/SecureInvocation$5.class    |  Bin 1048 -> 0 bytes
 .../plugin/liveconnect/SecureInvocation$6.class    |  Bin 721 -> 0 bytes
 .../plugin/liveconnect/SecureInvocation$7.class    |  Bin 845 -> 0 bytes
 .../sun/plugin/liveconnect/SecureInvocation.class  |  Bin 6457 -> 0 bytes
 .../plugin/liveconnect/SecurityContextHelper.class |  Bin 809 -> 0 bytes
 .../plugin/sun/plugin/navig/motif/AThread.class    |  Bin 854 -> 0 bytes
 .../plugin/sun/plugin/navig/motif/OJIPlugin.class  |  Bin 2264 -> 0 bytes
 .../plugin/sun/plugin/navig/motif/Plugin$1.class   |  Bin 212 -> 0 bytes
 .../sun/plugin/navig/motif/Plugin$Watcher.class    |  Bin 950 -> 0 bytes
 .../plugin/sun/plugin/navig/motif/Plugin.class     |  Bin 12679 -> 0 bytes
 .../plugin/sun/plugin/navig/motif/Worker.class     |  Bin 6533 -> 0 bytes
 .../net/cookie/MNetscape6CookieHandler.class       |  Bin 1236 -> 0 bytes
 .../plugin/net/cookie/Netscape4CookieHandler.class |  Bin 2461 -> 0 bytes
 .../protocol/jar/CachedJarURLConnection$1.class    |  Bin 1251 -> 0 bytes
 .../net/protocol/jar/CachedJarURLConnection.class  |  Bin 6077 -> 0 bytes
 .../sun/plugin/net/protocol/jar/Handler.class      |  Bin 511 -> 0 bytes
 .../net/proxy/MNetscape6BrowserProxyHandler.class  |  Bin 455 -> 0 bytes
 .../plugin/net/proxy/PluginAutoProxyHandler.class  |  Bin 2076 -> 0 bytes
 .../sun/plugin/net/proxy/PluginProxyInfo.class     |  Bin 697 -> 0 bytes
 .../net/proxy/PluginProxyServiceProvider.class     |  Bin 1195 -> 0 bytes
 .../sun/plugin/perf/PluginRollup$Event.class       |  Bin 965 -> 0 bytes
 .../sun/plugin/perf/PluginRollup$EventSet.class    |  Bin 1830 -> 0 bytes
 .../plugin/sun/plugin/perf/PluginRollup.class      |  Bin 5144 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator.class    |  Bin 13730 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator_de.class |  Bin 15091 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator_es.class |  Bin 15329 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator_fr.class |  Bin 15355 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator_it.class |  Bin 14643 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator_ja.class |  Bin 17518 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator_ko.class |  Bin 15496 -> 0 bytes
 .../plugin/sun/plugin/resources/Activator_sv.class |  Bin 14449 -> 0 bytes
 .../sun/plugin/resources/Activator_zh_CN.class     |  Bin 13834 -> 0 bytes
 .../sun/plugin/resources/Activator_zh_HK.class     |  Bin 13704 -> 0 bytes
 .../sun/plugin/resources/Activator_zh_TW.class     |  Bin 13704 -> 0 bytes
 .../sun/plugin/resources/ResourceHandler.class     |  Bin 1524 -> 0 bytes
 .../ActivatorSecurityManager$CheckPrint_1_2.class  |  Bin 763 -> 0 bytes
 .../plugin/security/ActivatorSecurityManager.class |  Bin 2040 -> 0 bytes
 .../sun/plugin/security/Broken11ClassFixer.class   |  Bin 6915 -> 0 bytes
 .../security/JDK11ClassFileTransformer$1.class     |  Bin 263 -> 0 bytes
 ...lassFileTransformer$Broken11Transformer_0.class |  Bin 1209 -> 0 bytes
 ...lassFileTransformer$Broken11Transformer_1.class |  Bin 904 -> 0 bytes
 .../security/JDK11ClassFileTransformer.class       |  Bin 1217 -> 0 bytes
 .../sun/plugin/security/PluginClassLoader$1.class  |  Bin 724 -> 0 bytes
 .../sun/plugin/security/PluginClassLoader.class    |  Bin 7655 -> 0 bytes
 .../security/StripClassFile$ByteStream.class       |  Bin 1803 -> 0 bytes
 .../security/StripClassFile$ConstantPool.class     |  Bin 2310 -> 0 bytes
 .../sun/plugin/security/StripClassFile.class       |  Bin 5933 -> 0 bytes
 .../sun/plugin/services/BrowserService.class       |  Bin 635 -> 0 bytes
 .../plugin/services/MNetscape4BrowserService.class |  Bin 3137 -> 0 bytes
 .../services/MNetscape6BrowserAuthenticator.class  |  Bin 775 -> 0 bytes
 .../services/MNetscape6BrowserService$1.class      |  Bin 868 -> 0 bytes
 .../plugin/services/MNetscape6BrowserService.class |  Bin 3813 -> 0 bytes
 .../sun/plugin/services/MPlatformService.class     |  Bin 245 -> 0 bytes
 .../sun/plugin/services/PlatformService.class      |  Bin 1422 -> 0 bytes
 .../plugin/sun/plugin/util/AnimationPanel$1.class  |  Bin 668 -> 0 bytes
 .../sun/plugin/util/AnimationPanel$2$1.class       |  Bin 602 -> 0 bytes
 .../plugin/sun/plugin/util/AnimationPanel$2.class  |  Bin 1308 -> 0 bytes
 .../sun/plugin/util/AnimationPanel$Wrapper$1.class |  Bin 693 -> 0 bytes
 .../sun/plugin/util/AnimationPanel$Wrapper.class   |  Bin 1281 -> 0 bytes
 .../plugin/sun/plugin/util/AnimationPanel.class    |  Bin 17988 -> 0 bytes
 .../plugin/sun/plugin/util/EventMulticaster.class  |  Bin 2051 -> 0 bytes
 .../plugin/sun/plugin/util/GrayBoxPainter$1.class  |  Bin 931 -> 0 bytes
 .../plugin/sun/plugin/util/GrayBoxPainter.class    |  Bin 7561 -> 0 bytes
 .../plugin/sun/plugin/util/GrayBoxPanel.class      |  Bin 4723 -> 0 bytes
 .../plugin/sun/plugin/util/JavaCupLogo-161.png     |  Bin 5764 -> 0 bytes
 .../plugin/sun/plugin/util/NotifierObject.class    |  Bin 389 -> 0 bytes
 .../plugin/sun/plugin/util/PluginConfig.class      |  Bin 1698 -> 0 bytes
 .../sun/plugin/util/PluginConsoleController.class  |  Bin 4802 -> 0 bytes
 .../plugin/sun/plugin/util/PluginSysUtil$1.class   |  Bin 545 -> 0 bytes
 .../util/PluginSysUtil$1AWTInvocationLock.class    |  Bin 396 -> 0 bytes
 .../plugin/sun/plugin/util/PluginSysUtil$2.class   |  Bin 1029 -> 0 bytes
 .../PluginSysUtil$AppContextCreatorThread.class    |  Bin 974 -> 0 bytes
 .../plugin/util/PluginSysUtil$DummyDialog$1.class  |  Bin 651 -> 0 bytes
 .../plugin/util/PluginSysUtil$DummyDialog.class    |  Bin 812 -> 0 bytes
 .../util/PluginSysUtil$SysExecutionThread.class    |  Bin 1309 -> 0 bytes
 .../plugin/sun/plugin/util/PluginSysUtil.class     |  Bin 5271 -> 0 bytes
 .../plugin/sun/plugin/util/ProgressMonitor.class   |  Bin 4132 -> 0 bytes
 libraries/plugin/sun/plugin/util/Trace.class       |  Bin 5440 -> 0 bytes
 libraries/plugin/sun/plugin/util/TraceFilter.class |  Bin 494 -> 0 bytes
 libraries/plugin/sun/plugin/util/UserProfile.class |  Bin 1469 -> 0 bytes
 libraries/plugin/sun/plugin/util/droptext100.png   |  Bin 3107 -> 0 bytes
 libraries/plugin/sun/plugin/util/droptext170.png   |  Bin 3335 -> 0 bytes
 libraries/plugin/sun/plugin/util/droptext300.png   |  Bin 3396 -> 0 bytes
 libraries/plugin/sun/plugin/util/graybox_error.gif |  Bin 1072 -> 0 bytes
 .../plugin/sun/plugin/util/java-watermark.gif      |  Bin 576 -> 0 bytes
 libraries/plugin/sun/plugin/util/javacom100.png    |  Bin 3417 -> 0 bytes
 libraries/plugin/sun/plugin/util/javacom170.png    |  Bin 3535 -> 0 bytes
 libraries/plugin/sun/plugin/util/javacom300.png    |  Bin 4049 -> 0 bytes
 libraries/plugin/sun/plugin/util/javaglow100.png   |  Bin 8656 -> 0 bytes
 libraries/plugin/sun/plugin/util/javaglow170.png   |  Bin 11367 -> 0 bytes
 libraries/plugin/sun/plugin/util/javaglow25.png    |  Bin 3302 -> 0 bytes
 libraries/plugin/sun/plugin/util/javaglow300.png   |  Bin 19764 -> 0 bytes
 libraries/plugin/sun/plugin/util/javalogo100.png   |  Bin 4962 -> 0 bytes
 libraries/plugin/sun/plugin/util/javalogo170.png   |  Bin 6125 -> 0 bytes
 libraries/plugin/sun/plugin/util/javalogo25.png    |  Bin 2956 -> 0 bytes
 libraries/plugin/sun/plugin/util/javalogo300.png   |  Bin 7946 -> 0 bytes
 .../sun/plugin/viewer/AppletPanelCache.class       |  Bin 1631 -> 0 bytes
 .../sun/plugin/viewer/LifeCycleManager.class       |  Bin 3664 -> 0 bytes
 .../sun/plugin/viewer/MNetscapePluginContext.class |  Bin 1244 -> 0 bytes
 .../plugin/viewer/MNetscapePluginObject$1.class    |  Bin 716 -> 0 bytes
 .../plugin/viewer/MNetscapePluginObject$2.class    |  Bin 932 -> 0 bytes
 .../viewer/MNetscapePluginObject$Initer.class      |  Bin 1258 -> 0 bytes
 .../sun/plugin/viewer/MNetscapePluginObject.class  |  Bin 10806 -> 0 bytes
 .../plugin/viewer/context/AppletAudioClip$1.class  |  Bin 1530 -> 0 bytes
 .../plugin/viewer/context/AppletAudioClip.class    |  Bin 2636 -> 0 bytes
 .../viewer/context/AppletAudioClipFactory$1.class  |  Bin 1807 -> 0 bytes
 .../viewer/context/AppletAudioClipFactory.class    |  Bin 730 -> 0 bytes
 .../viewer/context/AppletImageFactory$1.class      |  Bin 1806 -> 0 bytes
 .../plugin/viewer/context/AppletImageFactory.class |  Bin 1255 -> 0 bytes
 .../context/DefaultPluginAppletContext$1.class     |  Bin 1179 -> 0 bytes
 .../context/DefaultPluginAppletContext$2.class     |  Bin 1426 -> 0 bytes
 .../context/DefaultPluginAppletContext.class       |  Bin 7418 -> 0 bytes
 .../viewer/context/MNetscape6AppletContext.class   |  Bin 672 -> 0 bytes
 .../viewer/context/NetscapeAppletContext.class     |  Bin 1111 -> 0 bytes
 .../viewer/context/PluginAppletContext.class       |  Bin 360 -> 0 bytes
 .../plugin/viewer/context/PluginAudioClip.class    |  Bin 680 -> 0 bytes
 .../plugin/viewer/context/PluginBeansContext.class |  Bin 2116 -> 0 bytes
 .../viewer/frame/MNetscapeEmbeddedFrame.class      |  Bin 1226 -> 0 bytes
 .../viewer/frame/XNetscapeEmbeddedFrame.class      |  Bin 1333 -> 0 bytes
 manifest.mf                                        |    3 -
 nbproject/build-impl.xml                           |  894 -----
 nbproject/genfiles.properties                      |    8 -
 nbproject/private/config.properties                |    0
 nbproject/private/private.properties               |    6 -
 nbproject/private/private.xml                      |   10 -
 nbproject/project.properties                       |   81 -
 nbproject/project.xml                              |   16 -
 src/net/miginfocom/layout/AC.java                  |  564 +++
 src/net/miginfocom/layout/BoundSize.java           |  273 ++
 src/net/miginfocom/layout/CC.java                  | 1833 ++++++++++
 src/net/miginfocom/layout/ComponentWrapper.java    |  297 ++
 src/net/miginfocom/layout/ConstraintParser.java    | 1464 ++++++++
 src/net/miginfocom/layout/ContainerWrapper.java    |   69 +
 src/net/miginfocom/layout/DimConstraint.java       |  471 +++
 src/net/miginfocom/layout/Grid.java                | 2353 +++++++++++++
 src/net/miginfocom/layout/IDEUtil.java             |  789 +++++
 src/net/miginfocom/layout/InCellGapProvider.java   |   65 +
 src/net/miginfocom/layout/LC.java                  | 1032 ++++++
 src/net/miginfocom/layout/LayoutCallback.java      |   77 +
 src/net/miginfocom/layout/LayoutUtil.java          |  568 ++++
 src/net/miginfocom/layout/LinkHandler.java         |  200 ++
 src/net/miginfocom/layout/PlatformDefaults.java    |  772 +++++
 src/net/miginfocom/layout/ResizeConstraint.java    |   92 +
 src/net/miginfocom/layout/UnitConverter.java       |   59 +
 src/net/miginfocom/layout/UnitValue.java           |  637 ++++
 src/net/miginfocom/swing/MigLayout.java            |  701 ++++
 .../miginfocom/swing/SwingComponentWrapper.java    |  459 +++
 .../miginfocom/swing/SwingContainerWrapper.java    |  110 +
 src/netscape/javascript/JSException.class          |  Bin 1455 -> 0 bytes
 src/netscape/javascript/JSObject.class             |  Bin 1220 -> 0 bytes
 src/netscape/javascript/JSUtil.class               |  Bin 596 -> 0 bytes
 .../security/ForbiddenTargetException.class        |  Bin 336 -> 0 bytes
 src/netscape/security/ParameterizedTarget.class    |  Bin 1120 -> 0 bytes
 src/netscape/security/Principal.class              |  Bin 1644 -> 0 bytes
 src/netscape/security/Privilege.class              |  Bin 1752 -> 0 bytes
 src/netscape/security/PrivilegeManager.class       |  Bin 3331 -> 0 bytes
 src/netscape/security/PrivilegeTable.class         |  Bin 1458 -> 0 bytes
 src/netscape/security/Target.class                 |  Bin 2946 -> 0 bytes
 src/netscape/security/UserDialogHelper.class       |  Bin 701 -> 0 bytes
 src/netscape/security/UserTarget.class             |  Bin 995 -> 0 bytes
 src/opticalraytracer/Beep.java                     |   76 +
 src/opticalraytracer/ColorButton.java              |   81 +-
 src/opticalraytracer/ColorPos.java                 |   45 -
 src/opticalraytracer/Common.java                   |  222 ++
 src/opticalraytracer/ComplexInt.java               |   28 +
 src/opticalraytracer/ControlManager.java           |  364 ++
 src/opticalraytracer/ControlPanelManager.java      |  183 -
 src/opticalraytracer/DataTableCellRenderer.java    |   43 +
 src/opticalraytracer/DataTableDisplay.java         |   91 +
 src/opticalraytracer/DataTableModel.java           |  175 +
 .../{MutableInt.java => ElementBase.java}          |   30 +-
 src/opticalraytracer/ElementHyperbolic.java        |  294 ++
 src/opticalraytracer/ElementParabolic.java         |  306 ++
 src/opticalraytracer/ElementPlanar.java            |   56 +
 src/opticalraytracer/ElementSpherical.java         |  232 ++
 src/opticalraytracer/GraphicDisplay.form           |  126 -
 src/opticalraytracer/GraphicDisplay.java           | 1232 ++++---
 src/opticalraytracer/HelpState.java                |   13 +
 src/opticalraytracer/ImageTransferable.java        |   30 +-
 src/opticalraytracer/InitManager.java              |  361 --
 src/opticalraytracer/InitializationManager.java    |  137 +
 .../IntersectionSortComparator.java                |    4 +-
 src/opticalraytracer/Lens.java                     |  337 --
 src/opticalraytracer/LineAnalysis.java             |  270 ++
 src/opticalraytracer/LineData.java                 |   47 +
 src/opticalraytracer/LocaleHandler.java            |  103 +
 src/opticalraytracer/MutableDouble.java            |   39 -
 src/opticalraytracer/MyColor.java                  |   48 +-
 src/opticalraytracer/MyHelpPane.form               |  109 -
 src/opticalraytracer/MyHelpPane.java               |  752 ++--
 src/opticalraytracer/MyJFrame.java                 |   18 +
 .../{ComplexNum.java => ObjectValues.java}         |   41 +-
 src/opticalraytracer/OpticalComponent.java         |  553 +++
 src/opticalraytracer/OpticalRayTracer.form         | 1314 -------
 src/opticalraytracer/OpticalRayTracer.java         | 3592 +++++++++-----------
 src/opticalraytracer/OpticalRayTracerApp.form      |   26 -
 src/opticalraytracer/OpticalRayTracerApp.java      |   86 -
 src/opticalraytracer/OpticalRayTracerApplet.form   |   18 -
 src/opticalraytracer/OpticalRayTracerApplet.java   |  170 -
 src/opticalraytracer/ProgramControl.java           |    8 +
 .../{Box.java => ProgramValues.java}               |   75 +-
 src/opticalraytracer/RayLensIntersection.java      |   50 +-
 src/opticalraytracer/RayTraceComputer.java         |  961 +++---
 src/opticalraytracer/StringComparatorNoCase.java   |   34 -
 src/opticalraytracer/StringEscapeUnescape.java     |   57 -
 src/opticalraytracer/UndoRedoPackage.java          |   57 -
 src/opticalraytracer/UserActionManager.java        |  155 -
 src/opticalraytracer/ValueManager.java             |  123 +
 src/opticalraytracer/Vector.java                   |  173 +
 src/opticalraytracer/WavelengthColor.java          |   93 +
 src/opticalraytracer/applet_test_page.html         |   14 -
 src/opticalraytracer/help_resources/HelpText.html  |  644 ----
 src/opticalraytracer/help_resources/HelpText.txt   |  285 --
 src/opticalraytracer/helpresources/HelpText.html   | 1099 ++++++
 src/opticalraytracer/helpresources/HelpText.html~  | 1092 ++++++
 .../helpresources/dispersionTestLens1.txt          |   20 +
 .../helpresources/dispersionTestLens2.txt          |   20 +
 .../helpresources/dispersionTestSetup.txt          |  113 +
 .../helpresources/overlapDiagram.png               |  Bin 0 -> 29568 bytes
 src/opticalraytracer/icons/OpticalRayTracer.png    |  Bin 398 -> 5462 bytes
 437 files changed, 22111 insertions(+), 8553 deletions(-)

diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..fb565a5
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644
index 0000000..0436179
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>OpticalRayTracer</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/MANIFEST.mf b/MANIFEST.mf
new file mode 100644
index 0000000..adf8634
--- /dev/null
+++ b/MANIFEST.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: opticalraytracer.OpticalRayTracer
diff --git a/libraries/plugin.jar b/libraries/plugin.jar
deleted file mode 100644
index da3fa12..0000000
Binary files a/libraries/plugin.jar and /dev/null differ
diff --git a/libraries/plugin/netscape/javascript/JSException.class b/libraries/plugin/netscape/javascript/JSException.class
deleted file mode 100644
index 4d3f1d2..0000000
Binary files a/libraries/plugin/netscape/javascript/JSException.class and /dev/null differ
diff --git a/libraries/plugin/netscape/javascript/JSObject.class b/libraries/plugin/netscape/javascript/JSObject.class
deleted file mode 100644
index 8c24460..0000000
Binary files a/libraries/plugin/netscape/javascript/JSObject.class and /dev/null differ
diff --git a/libraries/plugin/netscape/javascript/JSUtil.class b/libraries/plugin/netscape/javascript/JSUtil.class
deleted file mode 100644
index 6395261..0000000
Binary files a/libraries/plugin/netscape/javascript/JSUtil.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/ForbiddenTargetException.class b/libraries/plugin/netscape/security/ForbiddenTargetException.class
deleted file mode 100644
index 9599a3f..0000000
Binary files a/libraries/plugin/netscape/security/ForbiddenTargetException.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/ParameterizedTarget.class b/libraries/plugin/netscape/security/ParameterizedTarget.class
deleted file mode 100644
index 255c200..0000000
Binary files a/libraries/plugin/netscape/security/ParameterizedTarget.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/Principal.class b/libraries/plugin/netscape/security/Principal.class
deleted file mode 100644
index 0861884..0000000
Binary files a/libraries/plugin/netscape/security/Principal.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/Privilege.class b/libraries/plugin/netscape/security/Privilege.class
deleted file mode 100644
index cbfd234..0000000
Binary files a/libraries/plugin/netscape/security/Privilege.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/PrivilegeManager.class b/libraries/plugin/netscape/security/PrivilegeManager.class
deleted file mode 100644
index 0961599..0000000
Binary files a/libraries/plugin/netscape/security/PrivilegeManager.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/PrivilegeTable.class b/libraries/plugin/netscape/security/PrivilegeTable.class
deleted file mode 100644
index fe4b12f..0000000
Binary files a/libraries/plugin/netscape/security/PrivilegeTable.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/Target.class b/libraries/plugin/netscape/security/Target.class
deleted file mode 100644
index 1c13ef5..0000000
Binary files a/libraries/plugin/netscape/security/Target.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/UserDialogHelper.class b/libraries/plugin/netscape/security/UserDialogHelper.class
deleted file mode 100644
index 465a88c..0000000
Binary files a/libraries/plugin/netscape/security/UserDialogHelper.class and /dev/null differ
diff --git a/libraries/plugin/netscape/security/UserTarget.class b/libraries/plugin/netscape/security/UserTarget.class
deleted file mode 100644
index a4adccc..0000000
Binary files a/libraries/plugin/netscape/security/UserTarget.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletObjectInputStream.class b/libraries/plugin/sun/plugin/AppletObjectInputStream.class
deleted file mode 100644
index 8ebf065..0000000
Binary files a/libraries/plugin/sun/plugin/AppletObjectInputStream.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletStatusListener.class b/libraries/plugin/sun/plugin/AppletStatusListener.class
deleted file mode 100644
index 0470844..0000000
Binary files a/libraries/plugin/sun/plugin/AppletStatusListener.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$1.class b/libraries/plugin/sun/plugin/AppletViewer$1.class
deleted file mode 100644
index 1cbab9d..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$2.class b/libraries/plugin/sun/plugin/AppletViewer$2.class
deleted file mode 100644
index 81cf051..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$3.class b/libraries/plugin/sun/plugin/AppletViewer$3.class
deleted file mode 100644
index d4d7c41..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$3.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$4.class b/libraries/plugin/sun/plugin/AppletViewer$4.class
deleted file mode 100644
index 210ab30..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$4.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$5.class b/libraries/plugin/sun/plugin/AppletViewer$5.class
deleted file mode 100644
index b50d759..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$5.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$6.class b/libraries/plugin/sun/plugin/AppletViewer$6.class
deleted file mode 100644
index afcabdf..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$6.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$7.class b/libraries/plugin/sun/plugin/AppletViewer$7.class
deleted file mode 100644
index e072748..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$7.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$AppletEventListener.class b/libraries/plugin/sun/plugin/AppletViewer$AppletEventListener.class
deleted file mode 100644
index de2bb93..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$AppletEventListener.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer$GrayBoxListener.class b/libraries/plugin/sun/plugin/AppletViewer$GrayBoxListener.class
deleted file mode 100644
index d81f27e..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer$GrayBoxListener.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/AppletViewer.class b/libraries/plugin/sun/plugin/AppletViewer.class
deleted file mode 100644
index 74fce6c..0000000
Binary files a/libraries/plugin/sun/plugin/AppletViewer.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/BeansApplet.class b/libraries/plugin/sun/plugin/BeansApplet.class
deleted file mode 100644
index 237092d..0000000
Binary files a/libraries/plugin/sun/plugin/BeansApplet.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/BeansViewer.class b/libraries/plugin/sun/plugin/BeansViewer.class
deleted file mode 100644
index 7a8e490..0000000
Binary files a/libraries/plugin/sun/plugin/BeansViewer.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/ClassLoaderInfo$1.class b/libraries/plugin/sun/plugin/ClassLoaderInfo$1.class
deleted file mode 100644
index 7c71ee8..0000000
Binary files a/libraries/plugin/sun/plugin/ClassLoaderInfo$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/ClassLoaderInfo$LoaderReference.class b/libraries/plugin/sun/plugin/ClassLoaderInfo$LoaderReference.class
deleted file mode 100644
index 7177ebb..0000000
Binary files a/libraries/plugin/sun/plugin/ClassLoaderInfo$LoaderReference.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/ClassLoaderInfo.class b/libraries/plugin/sun/plugin/ClassLoaderInfo.class
deleted file mode 100644
index b71b111..0000000
Binary files a/libraries/plugin/sun/plugin/ClassLoaderInfo.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/JavaRunTime$1.class b/libraries/plugin/sun/plugin/JavaRunTime$1.class
deleted file mode 100644
index 05677cf..0000000
Binary files a/libraries/plugin/sun/plugin/JavaRunTime$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/JavaRunTime$2.class b/libraries/plugin/sun/plugin/JavaRunTime$2.class
deleted file mode 100644
index 751669e..0000000
Binary files a/libraries/plugin/sun/plugin/JavaRunTime$2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/JavaRunTime.class b/libraries/plugin/sun/plugin/JavaRunTime.class
deleted file mode 100644
index 783181d..0000000
Binary files a/libraries/plugin/sun/plugin/JavaRunTime.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/PluginURLJarFileCallBack$1.class b/libraries/plugin/sun/plugin/PluginURLJarFileCallBack$1.class
deleted file mode 100644
index 741a83b..0000000
Binary files a/libraries/plugin/sun/plugin/PluginURLJarFileCallBack$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/PluginURLJarFileCallBack.class b/libraries/plugin/sun/plugin/PluginURLJarFileCallBack.class
deleted file mode 100644
index f7e678d..0000000
Binary files a/libraries/plugin/sun/plugin/PluginURLJarFileCallBack.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/WJcovUtil.class b/libraries/plugin/sun/plugin/WJcovUtil.class
deleted file mode 100644
index 4faea22..0000000
Binary files a/libraries/plugin/sun/plugin/WJcovUtil.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/cache/CacheUpdateHelper.class b/libraries/plugin/sun/plugin/cache/CacheUpdateHelper.class
deleted file mode 100644
index 295ef7c..0000000
Binary files a/libraries/plugin/sun/plugin/cache/CacheUpdateHelper.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/cache/FileVersion.class b/libraries/plugin/sun/plugin/cache/FileVersion.class
deleted file mode 100644
index 16fa150..0000000
Binary files a/libraries/plugin/sun/plugin/cache/FileVersion.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/cache/JarCacheUtil.class b/libraries/plugin/sun/plugin/cache/JarCacheUtil.class
deleted file mode 100644
index 0b24e2b..0000000
Binary files a/libraries/plugin/sun/plugin/cache/JarCacheUtil.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/cache/JarCacheVersionException.class b/libraries/plugin/sun/plugin/cache/JarCacheVersionException.class
deleted file mode 100644
index 000eba0..0000000
Binary files a/libraries/plugin/sun/plugin/cache/JarCacheVersionException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/cache/OldCacheEntry$1.class b/libraries/plugin/sun/plugin/cache/OldCacheEntry$1.class
deleted file mode 100644
index 062a6b8..0000000
Binary files a/libraries/plugin/sun/plugin/cache/OldCacheEntry$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/cache/OldCacheEntry.class b/libraries/plugin/sun/plugin/cache/OldCacheEntry.class
deleted file mode 100644
index 3601d73..0000000
Binary files a/libraries/plugin/sun/plugin/cache/OldCacheEntry.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMAccessor.class b/libraries/plugin/sun/plugin/dom/DOMAccessor.class
deleted file mode 100644
index fa60718..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMAccessor.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMImplementation.class b/libraries/plugin/sun/plugin/dom/DOMImplementation.class
deleted file mode 100644
index eb83bc0..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMImplementation.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMObject.class b/libraries/plugin/sun/plugin/dom/DOMObject.class
deleted file mode 100644
index 8b068ab..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMObject.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMObjectFactory.class b/libraries/plugin/sun/plugin/dom/DOMObjectFactory.class
deleted file mode 100644
index 33c6b1a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMObjectFactory.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMObjectHelper.class b/libraries/plugin/sun/plugin/dom/DOMObjectHelper.class
deleted file mode 100644
index e28b1d0..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMObjectHelper.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMObjectTypeHelper.class b/libraries/plugin/sun/plugin/dom/DOMObjectTypeHelper.class
deleted file mode 100644
index e3ba29f..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMObjectTypeHelper.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMService.class b/libraries/plugin/sun/plugin/dom/DOMService.class
deleted file mode 100644
index 8061e84..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMService.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/DOMServiceProvider.class b/libraries/plugin/sun/plugin/dom/DOMServiceProvider.class
deleted file mode 100644
index ccfa19a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/DOMServiceProvider.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/Attr.class b/libraries/plugin/sun/plugin/dom/core/Attr.class
deleted file mode 100644
index 4ba5f70..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/Attr.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/CharacterData.class b/libraries/plugin/sun/plugin/dom/core/CharacterData.class
deleted file mode 100644
index f0477b5..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/CharacterData.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/Comment.class b/libraries/plugin/sun/plugin/dom/core/Comment.class
deleted file mode 100644
index 207188a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/Comment.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/Document.class b/libraries/plugin/sun/plugin/dom/core/Document.class
deleted file mode 100644
index a2bf52e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/Document.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/DocumentFragment.class b/libraries/plugin/sun/plugin/dom/core/DocumentFragment.class
deleted file mode 100644
index 8010ebf..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/DocumentFragment.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/Element.class b/libraries/plugin/sun/plugin/dom/core/Element.class
deleted file mode 100644
index 024eda8..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/Element.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/Node.class b/libraries/plugin/sun/plugin/dom/core/Node.class
deleted file mode 100644
index 6574424..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/Node.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/core/Text.class b/libraries/plugin/sun/plugin/dom/core/Text.class
deleted file mode 100644
index 6dcd788..0000000
Binary files a/libraries/plugin/sun/plugin/dom/core/Text.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSCharsetRule.class b/libraries/plugin/sun/plugin/dom/css/CSSCharsetRule.class
deleted file mode 100644
index 20428e7..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSCharsetRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSConstants.class b/libraries/plugin/sun/plugin/dom/css/CSSConstants.class
deleted file mode 100644
index 44e6472..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSConstants.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSFontFaceRule.class b/libraries/plugin/sun/plugin/dom/css/CSSFontFaceRule.class
deleted file mode 100644
index d58c20d..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSFontFaceRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSImportRule.class b/libraries/plugin/sun/plugin/dom/css/CSSImportRule.class
deleted file mode 100644
index ac0b8d3..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSImportRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSMediaRule.class b/libraries/plugin/sun/plugin/dom/css/CSSMediaRule.class
deleted file mode 100644
index 23cd11e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSMediaRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSPageRule.class b/libraries/plugin/sun/plugin/dom/css/CSSPageRule.class
deleted file mode 100644
index 52dacfc..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSPageRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSPrimitiveValue.class b/libraries/plugin/sun/plugin/dom/css/CSSPrimitiveValue.class
deleted file mode 100644
index e13c5ff..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSPrimitiveValue.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSRule.class b/libraries/plugin/sun/plugin/dom/css/CSSRule.class
deleted file mode 100644
index 8fa999a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSRuleList.class b/libraries/plugin/sun/plugin/dom/css/CSSRuleList.class
deleted file mode 100644
index e6f0d46..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSRuleList.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSStyleDeclaration.class b/libraries/plugin/sun/plugin/dom/css/CSSStyleDeclaration.class
deleted file mode 100644
index 062f28a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSStyleDeclaration.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSStyleRule.class b/libraries/plugin/sun/plugin/dom/css/CSSStyleRule.class
deleted file mode 100644
index 954e112..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSStyleRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSStyleSheet.class b/libraries/plugin/sun/plugin/dom/css/CSSStyleSheet.class
deleted file mode 100644
index bd8cffc..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSStyleSheet.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSUnknownRule.class b/libraries/plugin/sun/plugin/dom/css/CSSUnknownRule.class
deleted file mode 100644
index eff03cd..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSUnknownRule.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSValue.class b/libraries/plugin/sun/plugin/dom/css/CSSValue.class
deleted file mode 100644
index ba56903..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSValue.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/CSSValueList.class b/libraries/plugin/sun/plugin/dom/css/CSSValueList.class
deleted file mode 100644
index f89b990..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/CSSValueList.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/RGBColor.class b/libraries/plugin/sun/plugin/dom/css/RGBColor.class
deleted file mode 100644
index 2b8eb8a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/RGBColor.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/Rect.class b/libraries/plugin/sun/plugin/dom/css/Rect.class
deleted file mode 100644
index 82cdbeb..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/Rect.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/css/ViewCSS.class b/libraries/plugin/sun/plugin/dom/css/ViewCSS.class
deleted file mode 100644
index 3650f64..0000000
Binary files a/libraries/plugin/sun/plugin/dom/css/ViewCSS.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/exception/BrowserNotSupportedException.class b/libraries/plugin/sun/plugin/dom/exception/BrowserNotSupportedException.class
deleted file mode 100644
index b7d84f2..0000000
Binary files a/libraries/plugin/sun/plugin/dom/exception/BrowserNotSupportedException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/exception/HierarchyRequestException.class b/libraries/plugin/sun/plugin/dom/exception/HierarchyRequestException.class
deleted file mode 100644
index c863c82..0000000
Binary files a/libraries/plugin/sun/plugin/dom/exception/HierarchyRequestException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/exception/InvalidAccessException.class b/libraries/plugin/sun/plugin/dom/exception/InvalidAccessException.class
deleted file mode 100644
index 2e7bc02..0000000
Binary files a/libraries/plugin/sun/plugin/dom/exception/InvalidAccessException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/exception/InvalidStateException.class b/libraries/plugin/sun/plugin/dom/exception/InvalidStateException.class
deleted file mode 100644
index dd2511f..0000000
Binary files a/libraries/plugin/sun/plugin/dom/exception/InvalidStateException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/exception/NoModificationAllowedException.class b/libraries/plugin/sun/plugin/dom/exception/NoModificationAllowedException.class
deleted file mode 100644
index 957ed6e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/exception/NoModificationAllowedException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/exception/PluginNotSupportedException.class b/libraries/plugin/sun/plugin/dom/exception/PluginNotSupportedException.class
deleted file mode 100644
index 3da674d..0000000
Binary files a/libraries/plugin/sun/plugin/dom/exception/PluginNotSupportedException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/exception/WrongDocumentException.class b/libraries/plugin/sun/plugin/dom/exception/WrongDocumentException.class
deleted file mode 100644
index bb13338..0000000
Binary files a/libraries/plugin/sun/plugin/dom/exception/WrongDocumentException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLAnchorElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLAnchorElement.class
deleted file mode 100644
index fcfd939..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLAnchorElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLAppletElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLAppletElement.class
deleted file mode 100644
index 44ca7ed..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLAppletElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLAreaElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLAreaElement.class
deleted file mode 100644
index 7ad75d9..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLAreaElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLBRElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLBRElement.class
deleted file mode 100644
index 704c013..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLBRElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLBaseElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLBaseElement.class
deleted file mode 100644
index f07c7fb..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLBaseElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLBaseFontElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLBaseFontElement.class
deleted file mode 100644
index b93001d..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLBaseFontElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLBodyElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLBodyElement.class
deleted file mode 100644
index d33bb5e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLBodyElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLButtonElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLButtonElement.class
deleted file mode 100644
index d69609d..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLButtonElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLCollection.class b/libraries/plugin/sun/plugin/dom/html/HTMLCollection.class
deleted file mode 100644
index 93273c0..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLCollection.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLConstants.class b/libraries/plugin/sun/plugin/dom/html/HTMLConstants.class
deleted file mode 100644
index 373b574..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLConstants.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLDListElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLDListElement.class
deleted file mode 100644
index 3f26b4a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLDListElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLDirectoryElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLDirectoryElement.class
deleted file mode 100644
index 0920652..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLDirectoryElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLDivElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLDivElement.class
deleted file mode 100644
index 8a44fe1..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLDivElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLDocument.class b/libraries/plugin/sun/plugin/dom/html/HTMLDocument.class
deleted file mode 100644
index 657767e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLDocument.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLElement.class
deleted file mode 100644
index b001aff..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLFieldSetElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLFieldSetElement.class
deleted file mode 100644
index 3c8de84..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLFieldSetElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLFontElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLFontElement.class
deleted file mode 100644
index 022598e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLFontElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLFormElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLFormElement.class
deleted file mode 100644
index 37ff52d..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLFormElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLFrameElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLFrameElement.class
deleted file mode 100644
index 5543c3c..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLFrameElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLFrameSetElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLFrameSetElement.class
deleted file mode 100644
index 4cec5ca..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLFrameSetElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLHRElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLHRElement.class
deleted file mode 100644
index ae13398..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLHRElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLHeadElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLHeadElement.class
deleted file mode 100644
index a757390..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLHeadElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLHeadingElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLHeadingElement.class
deleted file mode 100644
index 7ef05fe..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLHeadingElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLHtmlElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLHtmlElement.class
deleted file mode 100644
index 8fd72c4..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLHtmlElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLIFrameElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLIFrameElement.class
deleted file mode 100644
index 6b1d99c..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLIFrameElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLImageElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLImageElement.class
deleted file mode 100644
index e011cff..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLImageElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLInputElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLInputElement.class
deleted file mode 100644
index ecee09a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLInputElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLIsIndexElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLIsIndexElement.class
deleted file mode 100644
index ad38c45..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLIsIndexElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLLIElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLLIElement.class
deleted file mode 100644
index 39a96d9..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLLIElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLLabelElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLLabelElement.class
deleted file mode 100644
index c661d1a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLLabelElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLLegendElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLLegendElement.class
deleted file mode 100644
index 11e047e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLLegendElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLLinkElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLLinkElement.class
deleted file mode 100644
index 73b02f2..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLLinkElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLMapElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLMapElement.class
deleted file mode 100644
index b7afc0d..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLMapElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLMenuElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLMenuElement.class
deleted file mode 100644
index 8b1e9dd..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLMenuElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLMetaElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLMetaElement.class
deleted file mode 100644
index 48b95b5..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLMetaElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLModElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLModElement.class
deleted file mode 100644
index 17f0cc8..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLModElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLOListElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLOListElement.class
deleted file mode 100644
index b949abb..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLOListElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLObjectElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLObjectElement.class
deleted file mode 100644
index 3d3da6c..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLObjectElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLOptGroupElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLOptGroupElement.class
deleted file mode 100644
index e3c114b..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLOptGroupElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLOptionElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLOptionElement.class
deleted file mode 100644
index 00cdf4e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLOptionElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLParagraphElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLParagraphElement.class
deleted file mode 100644
index f031e2e..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLParagraphElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLParamElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLParamElement.class
deleted file mode 100644
index a359e79..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLParamElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLPreElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLPreElement.class
deleted file mode 100644
index eee9a82..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLPreElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLQuoteElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLQuoteElement.class
deleted file mode 100644
index ae70a93..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLQuoteElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLScriptElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLScriptElement.class
deleted file mode 100644
index 1db1ff8..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLScriptElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLSelectElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLSelectElement.class
deleted file mode 100644
index c702aef..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLSelectElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLStyleElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLStyleElement.class
deleted file mode 100644
index 3d46bd0..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLStyleElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTableCaptionElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTableCaptionElement.class
deleted file mode 100644
index e8bab58..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTableCaptionElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTableCellElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTableCellElement.class
deleted file mode 100644
index ada7a7a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTableCellElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTableColElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTableColElement.class
deleted file mode 100644
index 38fb1f5..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTableColElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTableElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTableElement.class
deleted file mode 100644
index 2c83141..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTableElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTableRowElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTableRowElement.class
deleted file mode 100644
index 687e8d3..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTableRowElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTableSectionElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTableSectionElement.class
deleted file mode 100644
index e5fe25a..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTableSectionElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTextAreaElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTextAreaElement.class
deleted file mode 100644
index c55ddfe..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTextAreaElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLTitleElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLTitleElement.class
deleted file mode 100644
index 01ba052..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLTitleElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/HTMLUListElement.class b/libraries/plugin/sun/plugin/dom/html/HTMLUListElement.class
deleted file mode 100644
index 880bfab..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/HTMLUListElement.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/common/HTMLCollection.class b/libraries/plugin/sun/plugin/dom/html/common/HTMLCollection.class
deleted file mode 100644
index c22dbb1..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/common/HTMLCollection.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLAnchorCollection.class b/libraries/plugin/sun/plugin/dom/html/ns4/HTMLAnchorCollection.class
deleted file mode 100644
index 20c5533..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLAnchorCollection.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLAppletCollection.class b/libraries/plugin/sun/plugin/dom/html/ns4/HTMLAppletCollection.class
deleted file mode 100644
index e8b788c..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLAppletCollection.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLFormCollection.class b/libraries/plugin/sun/plugin/dom/html/ns4/HTMLFormCollection.class
deleted file mode 100644
index 1ff4be5..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLFormCollection.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLImageCollection.class b/libraries/plugin/sun/plugin/dom/html/ns4/HTMLImageCollection.class
deleted file mode 100644
index ff48029..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/ns4/HTMLImageCollection.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/html/ns4/NS4DOMObject.class b/libraries/plugin/sun/plugin/dom/html/ns4/NS4DOMObject.class
deleted file mode 100644
index b4db9f4..0000000
Binary files a/libraries/plugin/sun/plugin/dom/html/ns4/NS4DOMObject.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/stylesheets/MediaList.class b/libraries/plugin/sun/plugin/dom/stylesheets/MediaList.class
deleted file mode 100644
index 9e6e745..0000000
Binary files a/libraries/plugin/sun/plugin/dom/stylesheets/MediaList.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/stylesheets/SSLConstants.class b/libraries/plugin/sun/plugin/dom/stylesheets/SSLConstants.class
deleted file mode 100644
index 1fe1457..0000000
Binary files a/libraries/plugin/sun/plugin/dom/stylesheets/SSLConstants.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/stylesheets/StyleSheet.class b/libraries/plugin/sun/plugin/dom/stylesheets/StyleSheet.class
deleted file mode 100644
index bd51e9d..0000000
Binary files a/libraries/plugin/sun/plugin/dom/stylesheets/StyleSheet.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/stylesheets/StyleSheetList.class b/libraries/plugin/sun/plugin/dom/stylesheets/StyleSheetList.class
deleted file mode 100644
index a6a35b6..0000000
Binary files a/libraries/plugin/sun/plugin/dom/stylesheets/StyleSheetList.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/dom/views/AbstractView.class b/libraries/plugin/sun/plugin/dom/views/AbstractView.class
deleted file mode 100644
index 423e9be..0000000
Binary files a/libraries/plugin/sun/plugin/dom/views/AbstractView.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/ExtensionInstallationImpl$1.class b/libraries/plugin/sun/plugin/extension/ExtensionInstallationImpl$1.class
deleted file mode 100644
index 4bcd86e..0000000
Binary files a/libraries/plugin/sun/plugin/extension/ExtensionInstallationImpl$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/ExtensionInstallationImpl.class b/libraries/plugin/sun/plugin/extension/ExtensionInstallationImpl.class
deleted file mode 100644
index c9910d4..0000000
Binary files a/libraries/plugin/sun/plugin/extension/ExtensionInstallationImpl.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/ExtensionInstaller.class b/libraries/plugin/sun/plugin/extension/ExtensionInstaller.class
deleted file mode 100644
index 460ee5a..0000000
Binary files a/libraries/plugin/sun/plugin/extension/ExtensionInstaller.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/ExtensionUtils$1.class b/libraries/plugin/sun/plugin/extension/ExtensionUtils$1.class
deleted file mode 100644
index 22b7512..0000000
Binary files a/libraries/plugin/sun/plugin/extension/ExtensionUtils$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/ExtensionUtils.class b/libraries/plugin/sun/plugin/extension/ExtensionUtils.class
deleted file mode 100644
index f279ef8..0000000
Binary files a/libraries/plugin/sun/plugin/extension/ExtensionUtils.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/JavaExtensionInstaller.class b/libraries/plugin/sun/plugin/extension/JavaExtensionInstaller.class
deleted file mode 100644
index dd49b50..0000000
Binary files a/libraries/plugin/sun/plugin/extension/JavaExtensionInstaller.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/NativeExtensionInstaller.class b/libraries/plugin/sun/plugin/extension/NativeExtensionInstaller.class
deleted file mode 100644
index e333ad9..0000000
Binary files a/libraries/plugin/sun/plugin/extension/NativeExtensionInstaller.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/extension/RawExtensionInstaller.class b/libraries/plugin/sun/plugin/extension/RawExtensionInstaller.class
deleted file mode 100644
index 6c5f7d0..0000000
Binary files a/libraries/plugin/sun/plugin/extension/RawExtensionInstaller.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/JSClassLoader$1.class b/libraries/plugin/sun/plugin/javascript/JSClassLoader$1.class
deleted file mode 100644
index 3a568b3..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/JSClassLoader$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/JSClassLoader.class b/libraries/plugin/sun/plugin/javascript/JSClassLoader.class
deleted file mode 100644
index 3506190..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/JSClassLoader.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/JSContext.class b/libraries/plugin/sun/plugin/javascript/JSContext.class
deleted file mode 100644
index 8f604c2..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/JSContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/JSInvoke.class b/libraries/plugin/sun/plugin/javascript/JSInvoke.class
deleted file mode 100644
index 3b4f780..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/JSInvoke.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/JSObject.class b/libraries/plugin/sun/plugin/javascript/JSObject.class
deleted file mode 100644
index 761020e..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/JSObject.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/ReflectUtil.class b/libraries/plugin/sun/plugin/javascript/ReflectUtil.class
deleted file mode 100644
index 2308bdc..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/ReflectUtil.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Anchor.class b/libraries/plugin/sun/plugin/javascript/navig/Anchor.class
deleted file mode 100644
index c8908be..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Anchor.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/AnchorArray.class b/libraries/plugin/sun/plugin/javascript/navig/AnchorArray.class
deleted file mode 100644
index 5586d64..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/AnchorArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Array.class b/libraries/plugin/sun/plugin/javascript/navig/Array.class
deleted file mode 100644
index 2b53a49..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Array.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Document.class b/libraries/plugin/sun/plugin/javascript/navig/Document.class
deleted file mode 100644
index 21b6157..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Document.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Element.class b/libraries/plugin/sun/plugin/javascript/navig/Element.class
deleted file mode 100644
index 6c68f56..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Element.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/ElementArray.class b/libraries/plugin/sun/plugin/javascript/navig/ElementArray.class
deleted file mode 100644
index 9668e15..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/ElementArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Form.class b/libraries/plugin/sun/plugin/javascript/navig/Form.class
deleted file mode 100644
index 2cc8abe..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Form.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/FormArray.class b/libraries/plugin/sun/plugin/javascript/navig/FormArray.class
deleted file mode 100644
index f9c2e3a..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/FormArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/FrameArray.class b/libraries/plugin/sun/plugin/javascript/navig/FrameArray.class
deleted file mode 100644
index 27e1a5e..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/FrameArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/History.class b/libraries/plugin/sun/plugin/javascript/navig/History.class
deleted file mode 100644
index db28ab8..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/History.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Image.class b/libraries/plugin/sun/plugin/javascript/navig/Image.class
deleted file mode 100644
index 5860914..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Image.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/ImageArray.class b/libraries/plugin/sun/plugin/javascript/navig/ImageArray.class
deleted file mode 100644
index eaaf28e..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/ImageArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/JSObject.class b/libraries/plugin/sun/plugin/javascript/navig/JSObject.class
deleted file mode 100644
index 211b13c..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/JSObject.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/JSObjectFactory.class b/libraries/plugin/sun/plugin/javascript/navig/JSObjectFactory.class
deleted file mode 100644
index 73b008c..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/JSObjectFactory.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/JSObjectResolver.class b/libraries/plugin/sun/plugin/javascript/navig/JSObjectResolver.class
deleted file mode 100644
index ec80e9d..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/JSObjectResolver.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/JSType.class b/libraries/plugin/sun/plugin/javascript/navig/JSType.class
deleted file mode 100644
index 2c0cb40..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/JSType.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Link.class b/libraries/plugin/sun/plugin/javascript/navig/Link.class
deleted file mode 100644
index 9b8aba9..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Link.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/LinkArray.class b/libraries/plugin/sun/plugin/javascript/navig/LinkArray.class
deleted file mode 100644
index b07fd09..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/LinkArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Location.class b/libraries/plugin/sun/plugin/javascript/navig/Location.class
deleted file mode 100644
index 75c4b28..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Location.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Navigator.class b/libraries/plugin/sun/plugin/javascript/navig/Navigator.class
deleted file mode 100644
index f11f238..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Navigator.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Option.class b/libraries/plugin/sun/plugin/javascript/navig/Option.class
deleted file mode 100644
index c421b9a..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Option.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/OptionArray.class b/libraries/plugin/sun/plugin/javascript/navig/OptionArray.class
deleted file mode 100644
index 8d9d197..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/OptionArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/URL.class b/libraries/plugin/sun/plugin/javascript/navig/URL.class
deleted file mode 100644
index 3f053d1..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/URL.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig/Window.class b/libraries/plugin/sun/plugin/javascript/navig/Window.class
deleted file mode 100644
index db76f70..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig/Window.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/Anchor.class b/libraries/plugin/sun/plugin/javascript/navig4/Anchor.class
deleted file mode 100644
index 5ac0cba..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/Anchor.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/Document.class b/libraries/plugin/sun/plugin/javascript/navig4/Document.class
deleted file mode 100644
index 16b562c..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/Document.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/JSObjectFactory.class b/libraries/plugin/sun/plugin/javascript/navig4/JSObjectFactory.class
deleted file mode 100644
index df78263..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/JSObjectFactory.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/Layer.class b/libraries/plugin/sun/plugin/javascript/navig4/Layer.class
deleted file mode 100644
index 84cb594..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/Layer.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/LayerArray.class b/libraries/plugin/sun/plugin/javascript/navig4/LayerArray.class
deleted file mode 100644
index 3dc68dd..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/LayerArray.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/Link.class b/libraries/plugin/sun/plugin/javascript/navig4/Link.class
deleted file mode 100644
index 304ce57..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/Link.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/Navigator.class b/libraries/plugin/sun/plugin/javascript/navig4/Navigator.class
deleted file mode 100644
index e9142ae..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/Navigator.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/UIBar.class b/libraries/plugin/sun/plugin/javascript/navig4/UIBar.class
deleted file mode 100644
index 0aa2f00..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/UIBar.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig4/Window.class b/libraries/plugin/sun/plugin/javascript/navig4/Window.class
deleted file mode 100644
index 1589801..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig4/Window.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig5/JSObject.class b/libraries/plugin/sun/plugin/javascript/navig5/JSObject.class
deleted file mode 100644
index f4e476b..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig5/JSObject.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig5/SecurityContext$PrivilegedBlockAction.class b/libraries/plugin/sun/plugin/javascript/navig5/SecurityContext$PrivilegedBlockAction.class
deleted file mode 100644
index 1e67ccc..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig5/SecurityContext$PrivilegedBlockAction.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/javascript/navig5/SecurityContext.class b/libraries/plugin/sun/plugin/javascript/navig5/SecurityContext.class
deleted file mode 100644
index 4722845..0000000
Binary files a/libraries/plugin/sun/plugin/javascript/navig5/SecurityContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/JavaScriptPermission.class b/libraries/plugin/sun/plugin/liveconnect/JavaScriptPermission.class
deleted file mode 100644
index 8852496..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/JavaScriptPermission.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/JavaScriptProtectionDomain.class b/libraries/plugin/sun/plugin/liveconnect/JavaScriptProtectionDomain.class
deleted file mode 100644
index 64b6cf5..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/JavaScriptProtectionDomain.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/LiveConnect.class b/libraries/plugin/sun/plugin/liveconnect/LiveConnect.class
deleted file mode 100644
index 9dacfce..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/LiveConnect.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/OriginNotAllowedException.class b/libraries/plugin/sun/plugin/liveconnect/OriginNotAllowedException.class
deleted file mode 100644
index b76990c..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/OriginNotAllowedException.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/PrivilegedCallMethodAction.class b/libraries/plugin/sun/plugin/liveconnect/PrivilegedCallMethodAction.class
deleted file mode 100644
index 5160d38..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/PrivilegedCallMethodAction.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/PrivilegedConstructObjectAction.class b/libraries/plugin/sun/plugin/liveconnect/PrivilegedConstructObjectAction.class
deleted file mode 100644
index 0dd0a15..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/PrivilegedConstructObjectAction.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/PrivilegedGetFieldAction.class b/libraries/plugin/sun/plugin/liveconnect/PrivilegedGetFieldAction.class
deleted file mode 100644
index 5e60571..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/PrivilegedGetFieldAction.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/PrivilegedSetFieldAction.class b/libraries/plugin/sun/plugin/liveconnect/PrivilegedSetFieldAction.class
deleted file mode 100644
index df1987a..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/PrivilegedSetFieldAction.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/ReplaceMethod.class b/libraries/plugin/sun/plugin/liveconnect/ReplaceMethod.class
deleted file mode 100644
index 22f44c5..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/ReplaceMethod.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$1.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$1.class
deleted file mode 100644
index a2aba4c..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$2.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$2.class
deleted file mode 100644
index aba0631..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$3.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$3.class
deleted file mode 100644
index 7b244a5..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$3.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$4.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$4.class
deleted file mode 100644
index 8822a1a..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$4.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$5.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$5.class
deleted file mode 100644
index b8e1a96..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$5.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$6.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$6.class
deleted file mode 100644
index 5e03f9f..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$6.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$7.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$7.class
deleted file mode 100644
index b91c776..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation$7.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation.class b/libraries/plugin/sun/plugin/liveconnect/SecureInvocation.class
deleted file mode 100644
index e8d71d6..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecureInvocation.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/liveconnect/SecurityContextHelper.class b/libraries/plugin/sun/plugin/liveconnect/SecurityContextHelper.class
deleted file mode 100644
index 770ea3b..0000000
Binary files a/libraries/plugin/sun/plugin/liveconnect/SecurityContextHelper.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/navig/motif/AThread.class b/libraries/plugin/sun/plugin/navig/motif/AThread.class
deleted file mode 100644
index 8f740e3..0000000
Binary files a/libraries/plugin/sun/plugin/navig/motif/AThread.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/navig/motif/OJIPlugin.class b/libraries/plugin/sun/plugin/navig/motif/OJIPlugin.class
deleted file mode 100644
index 5c4ea1b..0000000
Binary files a/libraries/plugin/sun/plugin/navig/motif/OJIPlugin.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/navig/motif/Plugin$1.class b/libraries/plugin/sun/plugin/navig/motif/Plugin$1.class
deleted file mode 100644
index 8812831..0000000
Binary files a/libraries/plugin/sun/plugin/navig/motif/Plugin$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/navig/motif/Plugin$Watcher.class b/libraries/plugin/sun/plugin/navig/motif/Plugin$Watcher.class
deleted file mode 100644
index bf97b53..0000000
Binary files a/libraries/plugin/sun/plugin/navig/motif/Plugin$Watcher.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/navig/motif/Plugin.class b/libraries/plugin/sun/plugin/navig/motif/Plugin.class
deleted file mode 100644
index b855dca..0000000
Binary files a/libraries/plugin/sun/plugin/navig/motif/Plugin.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/navig/motif/Worker.class b/libraries/plugin/sun/plugin/navig/motif/Worker.class
deleted file mode 100644
index d31b1c2..0000000
Binary files a/libraries/plugin/sun/plugin/navig/motif/Worker.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/cookie/MNetscape6CookieHandler.class b/libraries/plugin/sun/plugin/net/cookie/MNetscape6CookieHandler.class
deleted file mode 100644
index 9ec26d8..0000000
Binary files a/libraries/plugin/sun/plugin/net/cookie/MNetscape6CookieHandler.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/cookie/Netscape4CookieHandler.class b/libraries/plugin/sun/plugin/net/cookie/Netscape4CookieHandler.class
deleted file mode 100644
index 9f50f4b..0000000
Binary files a/libraries/plugin/sun/plugin/net/cookie/Netscape4CookieHandler.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/protocol/jar/CachedJarURLConnection$1.class b/libraries/plugin/sun/plugin/net/protocol/jar/CachedJarURLConnection$1.class
deleted file mode 100644
index 918855f..0000000
Binary files a/libraries/plugin/sun/plugin/net/protocol/jar/CachedJarURLConnection$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/protocol/jar/CachedJarURLConnection.class b/libraries/plugin/sun/plugin/net/protocol/jar/CachedJarURLConnection.class
deleted file mode 100644
index ae85cbc..0000000
Binary files a/libraries/plugin/sun/plugin/net/protocol/jar/CachedJarURLConnection.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/protocol/jar/Handler.class b/libraries/plugin/sun/plugin/net/protocol/jar/Handler.class
deleted file mode 100644
index 24fa74d..0000000
Binary files a/libraries/plugin/sun/plugin/net/protocol/jar/Handler.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/proxy/MNetscape6BrowserProxyHandler.class b/libraries/plugin/sun/plugin/net/proxy/MNetscape6BrowserProxyHandler.class
deleted file mode 100644
index 9aacf18..0000000
Binary files a/libraries/plugin/sun/plugin/net/proxy/MNetscape6BrowserProxyHandler.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/proxy/PluginAutoProxyHandler.class b/libraries/plugin/sun/plugin/net/proxy/PluginAutoProxyHandler.class
deleted file mode 100644
index bf858b5..0000000
Binary files a/libraries/plugin/sun/plugin/net/proxy/PluginAutoProxyHandler.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/proxy/PluginProxyInfo.class b/libraries/plugin/sun/plugin/net/proxy/PluginProxyInfo.class
deleted file mode 100644
index f928f20..0000000
Binary files a/libraries/plugin/sun/plugin/net/proxy/PluginProxyInfo.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/net/proxy/PluginProxyServiceProvider.class b/libraries/plugin/sun/plugin/net/proxy/PluginProxyServiceProvider.class
deleted file mode 100644
index 2de0f31..0000000
Binary files a/libraries/plugin/sun/plugin/net/proxy/PluginProxyServiceProvider.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/perf/PluginRollup$Event.class b/libraries/plugin/sun/plugin/perf/PluginRollup$Event.class
deleted file mode 100644
index 75a0282..0000000
Binary files a/libraries/plugin/sun/plugin/perf/PluginRollup$Event.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/perf/PluginRollup$EventSet.class b/libraries/plugin/sun/plugin/perf/PluginRollup$EventSet.class
deleted file mode 100644
index 3668165..0000000
Binary files a/libraries/plugin/sun/plugin/perf/PluginRollup$EventSet.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/perf/PluginRollup.class b/libraries/plugin/sun/plugin/perf/PluginRollup.class
deleted file mode 100644
index a2dff2c..0000000
Binary files a/libraries/plugin/sun/plugin/perf/PluginRollup.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator.class b/libraries/plugin/sun/plugin/resources/Activator.class
deleted file mode 100644
index e8c769d..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_de.class b/libraries/plugin/sun/plugin/resources/Activator_de.class
deleted file mode 100644
index 2656288..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_de.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_es.class b/libraries/plugin/sun/plugin/resources/Activator_es.class
deleted file mode 100644
index 0a786a2..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_es.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_fr.class b/libraries/plugin/sun/plugin/resources/Activator_fr.class
deleted file mode 100644
index a8ede3f..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_fr.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_it.class b/libraries/plugin/sun/plugin/resources/Activator_it.class
deleted file mode 100644
index 1059c0c..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_it.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_ja.class b/libraries/plugin/sun/plugin/resources/Activator_ja.class
deleted file mode 100644
index e179240..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_ja.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_ko.class b/libraries/plugin/sun/plugin/resources/Activator_ko.class
deleted file mode 100644
index ddaefe3..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_ko.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_sv.class b/libraries/plugin/sun/plugin/resources/Activator_sv.class
deleted file mode 100644
index 3dcdf3b..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_sv.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_zh_CN.class b/libraries/plugin/sun/plugin/resources/Activator_zh_CN.class
deleted file mode 100644
index f39255e..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_zh_CN.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_zh_HK.class b/libraries/plugin/sun/plugin/resources/Activator_zh_HK.class
deleted file mode 100644
index 19ad691..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_zh_HK.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/Activator_zh_TW.class b/libraries/plugin/sun/plugin/resources/Activator_zh_TW.class
deleted file mode 100644
index 4902c44..0000000
Binary files a/libraries/plugin/sun/plugin/resources/Activator_zh_TW.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/resources/ResourceHandler.class b/libraries/plugin/sun/plugin/resources/ResourceHandler.class
deleted file mode 100644
index ce6e570..0000000
Binary files a/libraries/plugin/sun/plugin/resources/ResourceHandler.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/ActivatorSecurityManager$CheckPrint_1_2.class b/libraries/plugin/sun/plugin/security/ActivatorSecurityManager$CheckPrint_1_2.class
deleted file mode 100644
index 8e7dc9e..0000000
Binary files a/libraries/plugin/sun/plugin/security/ActivatorSecurityManager$CheckPrint_1_2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/ActivatorSecurityManager.class b/libraries/plugin/sun/plugin/security/ActivatorSecurityManager.class
deleted file mode 100644
index 2d55144..0000000
Binary files a/libraries/plugin/sun/plugin/security/ActivatorSecurityManager.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/Broken11ClassFixer.class b/libraries/plugin/sun/plugin/security/Broken11ClassFixer.class
deleted file mode 100644
index 502585f..0000000
Binary files a/libraries/plugin/sun/plugin/security/Broken11ClassFixer.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$1.class b/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$1.class
deleted file mode 100644
index 70ad1bf..0000000
Binary files a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$Broken11Transformer_0.class b/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$Broken11Transformer_0.class
deleted file mode 100644
index 624bb76..0000000
Binary files a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$Broken11Transformer_0.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$Broken11Transformer_1.class b/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$Broken11Transformer_1.class
deleted file mode 100644
index 65fd23a..0000000
Binary files a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer$Broken11Transformer_1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer.class b/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer.class
deleted file mode 100644
index 454d5c5..0000000
Binary files a/libraries/plugin/sun/plugin/security/JDK11ClassFileTransformer.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/PluginClassLoader$1.class b/libraries/plugin/sun/plugin/security/PluginClassLoader$1.class
deleted file mode 100644
index 1ad406a..0000000
Binary files a/libraries/plugin/sun/plugin/security/PluginClassLoader$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/PluginClassLoader.class b/libraries/plugin/sun/plugin/security/PluginClassLoader.class
deleted file mode 100644
index ba3ece1..0000000
Binary files a/libraries/plugin/sun/plugin/security/PluginClassLoader.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/StripClassFile$ByteStream.class b/libraries/plugin/sun/plugin/security/StripClassFile$ByteStream.class
deleted file mode 100644
index b238198..0000000
Binary files a/libraries/plugin/sun/plugin/security/StripClassFile$ByteStream.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/StripClassFile$ConstantPool.class b/libraries/plugin/sun/plugin/security/StripClassFile$ConstantPool.class
deleted file mode 100644
index 4998151..0000000
Binary files a/libraries/plugin/sun/plugin/security/StripClassFile$ConstantPool.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/security/StripClassFile.class b/libraries/plugin/sun/plugin/security/StripClassFile.class
deleted file mode 100644
index 15b6b7d..0000000
Binary files a/libraries/plugin/sun/plugin/security/StripClassFile.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/services/BrowserService.class b/libraries/plugin/sun/plugin/services/BrowserService.class
deleted file mode 100644
index 5755c16..0000000
Binary files a/libraries/plugin/sun/plugin/services/BrowserService.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/services/MNetscape4BrowserService.class b/libraries/plugin/sun/plugin/services/MNetscape4BrowserService.class
deleted file mode 100644
index 4a4a583..0000000
Binary files a/libraries/plugin/sun/plugin/services/MNetscape4BrowserService.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/services/MNetscape6BrowserAuthenticator.class b/libraries/plugin/sun/plugin/services/MNetscape6BrowserAuthenticator.class
deleted file mode 100644
index 7f76de3..0000000
Binary files a/libraries/plugin/sun/plugin/services/MNetscape6BrowserAuthenticator.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/services/MNetscape6BrowserService$1.class b/libraries/plugin/sun/plugin/services/MNetscape6BrowserService$1.class
deleted file mode 100644
index 2dd0f1e..0000000
Binary files a/libraries/plugin/sun/plugin/services/MNetscape6BrowserService$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/services/MNetscape6BrowserService.class b/libraries/plugin/sun/plugin/services/MNetscape6BrowserService.class
deleted file mode 100644
index 63a2a85..0000000
Binary files a/libraries/plugin/sun/plugin/services/MNetscape6BrowserService.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/services/MPlatformService.class b/libraries/plugin/sun/plugin/services/MPlatformService.class
deleted file mode 100644
index 882a79d..0000000
Binary files a/libraries/plugin/sun/plugin/services/MPlatformService.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/services/PlatformService.class b/libraries/plugin/sun/plugin/services/PlatformService.class
deleted file mode 100644
index 572eead..0000000
Binary files a/libraries/plugin/sun/plugin/services/PlatformService.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/AnimationPanel$1.class b/libraries/plugin/sun/plugin/util/AnimationPanel$1.class
deleted file mode 100644
index b69775c..0000000
Binary files a/libraries/plugin/sun/plugin/util/AnimationPanel$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/AnimationPanel$2$1.class b/libraries/plugin/sun/plugin/util/AnimationPanel$2$1.class
deleted file mode 100644
index 497012e..0000000
Binary files a/libraries/plugin/sun/plugin/util/AnimationPanel$2$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/AnimationPanel$2.class b/libraries/plugin/sun/plugin/util/AnimationPanel$2.class
deleted file mode 100644
index 13eb67f..0000000
Binary files a/libraries/plugin/sun/plugin/util/AnimationPanel$2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/AnimationPanel$Wrapper$1.class b/libraries/plugin/sun/plugin/util/AnimationPanel$Wrapper$1.class
deleted file mode 100644
index 3d97ae4..0000000
Binary files a/libraries/plugin/sun/plugin/util/AnimationPanel$Wrapper$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/AnimationPanel$Wrapper.class b/libraries/plugin/sun/plugin/util/AnimationPanel$Wrapper.class
deleted file mode 100644
index eb0a188..0000000
Binary files a/libraries/plugin/sun/plugin/util/AnimationPanel$Wrapper.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/AnimationPanel.class b/libraries/plugin/sun/plugin/util/AnimationPanel.class
deleted file mode 100644
index 1870454..0000000
Binary files a/libraries/plugin/sun/plugin/util/AnimationPanel.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/EventMulticaster.class b/libraries/plugin/sun/plugin/util/EventMulticaster.class
deleted file mode 100644
index baccc85..0000000
Binary files a/libraries/plugin/sun/plugin/util/EventMulticaster.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/GrayBoxPainter$1.class b/libraries/plugin/sun/plugin/util/GrayBoxPainter$1.class
deleted file mode 100644
index ef8ecaf..0000000
Binary files a/libraries/plugin/sun/plugin/util/GrayBoxPainter$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/GrayBoxPainter.class b/libraries/plugin/sun/plugin/util/GrayBoxPainter.class
deleted file mode 100644
index a69f0e3..0000000
Binary files a/libraries/plugin/sun/plugin/util/GrayBoxPainter.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/GrayBoxPanel.class b/libraries/plugin/sun/plugin/util/GrayBoxPanel.class
deleted file mode 100644
index 5fae2d8..0000000
Binary files a/libraries/plugin/sun/plugin/util/GrayBoxPanel.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/JavaCupLogo-161.png b/libraries/plugin/sun/plugin/util/JavaCupLogo-161.png
deleted file mode 100644
index c9b9b58..0000000
Binary files a/libraries/plugin/sun/plugin/util/JavaCupLogo-161.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/NotifierObject.class b/libraries/plugin/sun/plugin/util/NotifierObject.class
deleted file mode 100644
index 9891e56..0000000
Binary files a/libraries/plugin/sun/plugin/util/NotifierObject.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginConfig.class b/libraries/plugin/sun/plugin/util/PluginConfig.class
deleted file mode 100644
index 584bb5b..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginConfig.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginConsoleController.class b/libraries/plugin/sun/plugin/util/PluginConsoleController.class
deleted file mode 100644
index ebe5a45..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginConsoleController.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil$1.class b/libraries/plugin/sun/plugin/util/PluginSysUtil$1.class
deleted file mode 100644
index 6da67c1..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil$1AWTInvocationLock.class b/libraries/plugin/sun/plugin/util/PluginSysUtil$1AWTInvocationLock.class
deleted file mode 100644
index cf386c5..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil$1AWTInvocationLock.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil$2.class b/libraries/plugin/sun/plugin/util/PluginSysUtil$2.class
deleted file mode 100644
index 6458f5a..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil$2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil$AppContextCreatorThread.class b/libraries/plugin/sun/plugin/util/PluginSysUtil$AppContextCreatorThread.class
deleted file mode 100644
index ea273b3..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil$AppContextCreatorThread.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil$DummyDialog$1.class b/libraries/plugin/sun/plugin/util/PluginSysUtil$DummyDialog$1.class
deleted file mode 100644
index 6fd1519..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil$DummyDialog$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil$DummyDialog.class b/libraries/plugin/sun/plugin/util/PluginSysUtil$DummyDialog.class
deleted file mode 100644
index c6b42c4..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil$DummyDialog.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil$SysExecutionThread.class b/libraries/plugin/sun/plugin/util/PluginSysUtil$SysExecutionThread.class
deleted file mode 100644
index 470776e..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil$SysExecutionThread.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/PluginSysUtil.class b/libraries/plugin/sun/plugin/util/PluginSysUtil.class
deleted file mode 100644
index 7244ecf..0000000
Binary files a/libraries/plugin/sun/plugin/util/PluginSysUtil.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/ProgressMonitor.class b/libraries/plugin/sun/plugin/util/ProgressMonitor.class
deleted file mode 100644
index baf83ea..0000000
Binary files a/libraries/plugin/sun/plugin/util/ProgressMonitor.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/Trace.class b/libraries/plugin/sun/plugin/util/Trace.class
deleted file mode 100644
index b547394..0000000
Binary files a/libraries/plugin/sun/plugin/util/Trace.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/TraceFilter.class b/libraries/plugin/sun/plugin/util/TraceFilter.class
deleted file mode 100644
index e647543..0000000
Binary files a/libraries/plugin/sun/plugin/util/TraceFilter.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/UserProfile.class b/libraries/plugin/sun/plugin/util/UserProfile.class
deleted file mode 100644
index 3ec3d9e..0000000
Binary files a/libraries/plugin/sun/plugin/util/UserProfile.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/droptext100.png b/libraries/plugin/sun/plugin/util/droptext100.png
deleted file mode 100644
index 3aa766b..0000000
Binary files a/libraries/plugin/sun/plugin/util/droptext100.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/droptext170.png b/libraries/plugin/sun/plugin/util/droptext170.png
deleted file mode 100644
index 0748356..0000000
Binary files a/libraries/plugin/sun/plugin/util/droptext170.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/droptext300.png b/libraries/plugin/sun/plugin/util/droptext300.png
deleted file mode 100644
index 24e98df..0000000
Binary files a/libraries/plugin/sun/plugin/util/droptext300.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/graybox_error.gif b/libraries/plugin/sun/plugin/util/graybox_error.gif
deleted file mode 100644
index 08c5837..0000000
Binary files a/libraries/plugin/sun/plugin/util/graybox_error.gif and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/java-watermark.gif b/libraries/plugin/sun/plugin/util/java-watermark.gif
deleted file mode 100644
index 35d8f07..0000000
Binary files a/libraries/plugin/sun/plugin/util/java-watermark.gif and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javacom100.png b/libraries/plugin/sun/plugin/util/javacom100.png
deleted file mode 100644
index 4e69e22..0000000
Binary files a/libraries/plugin/sun/plugin/util/javacom100.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javacom170.png b/libraries/plugin/sun/plugin/util/javacom170.png
deleted file mode 100644
index 1adb644..0000000
Binary files a/libraries/plugin/sun/plugin/util/javacom170.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javacom300.png b/libraries/plugin/sun/plugin/util/javacom300.png
deleted file mode 100644
index ce5bc20..0000000
Binary files a/libraries/plugin/sun/plugin/util/javacom300.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javaglow100.png b/libraries/plugin/sun/plugin/util/javaglow100.png
deleted file mode 100644
index bd1a62a..0000000
Binary files a/libraries/plugin/sun/plugin/util/javaglow100.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javaglow170.png b/libraries/plugin/sun/plugin/util/javaglow170.png
deleted file mode 100644
index 6ee29cf..0000000
Binary files a/libraries/plugin/sun/plugin/util/javaglow170.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javaglow25.png b/libraries/plugin/sun/plugin/util/javaglow25.png
deleted file mode 100644
index 9b1d1d5..0000000
Binary files a/libraries/plugin/sun/plugin/util/javaglow25.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javaglow300.png b/libraries/plugin/sun/plugin/util/javaglow300.png
deleted file mode 100644
index 1c2483e..0000000
Binary files a/libraries/plugin/sun/plugin/util/javaglow300.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javalogo100.png b/libraries/plugin/sun/plugin/util/javalogo100.png
deleted file mode 100644
index 882a74b..0000000
Binary files a/libraries/plugin/sun/plugin/util/javalogo100.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javalogo170.png b/libraries/plugin/sun/plugin/util/javalogo170.png
deleted file mode 100644
index 6909406..0000000
Binary files a/libraries/plugin/sun/plugin/util/javalogo170.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javalogo25.png b/libraries/plugin/sun/plugin/util/javalogo25.png
deleted file mode 100644
index 70386e3..0000000
Binary files a/libraries/plugin/sun/plugin/util/javalogo25.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/util/javalogo300.png b/libraries/plugin/sun/plugin/util/javalogo300.png
deleted file mode 100644
index db09be0..0000000
Binary files a/libraries/plugin/sun/plugin/util/javalogo300.png and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/AppletPanelCache.class b/libraries/plugin/sun/plugin/viewer/AppletPanelCache.class
deleted file mode 100644
index 1579c14..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/AppletPanelCache.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/LifeCycleManager.class b/libraries/plugin/sun/plugin/viewer/LifeCycleManager.class
deleted file mode 100644
index 1818bf9..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/LifeCycleManager.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/MNetscapePluginContext.class b/libraries/plugin/sun/plugin/viewer/MNetscapePluginContext.class
deleted file mode 100644
index 182ffe2..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/MNetscapePluginContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$1.class b/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$1.class
deleted file mode 100644
index cec4ea3..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$2.class b/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$2.class
deleted file mode 100644
index 87e93c9..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$Initer.class b/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$Initer.class
deleted file mode 100644
index 95d5b75..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject$Initer.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject.class b/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject.class
deleted file mode 100644
index 55d057f..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/MNetscapePluginObject.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClip$1.class b/libraries/plugin/sun/plugin/viewer/context/AppletAudioClip$1.class
deleted file mode 100644
index 6f72e9b..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClip$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClip.class b/libraries/plugin/sun/plugin/viewer/context/AppletAudioClip.class
deleted file mode 100644
index f7dfc2f..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClip.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClipFactory$1.class b/libraries/plugin/sun/plugin/viewer/context/AppletAudioClipFactory$1.class
deleted file mode 100644
index 61d4c49..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClipFactory$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClipFactory.class b/libraries/plugin/sun/plugin/viewer/context/AppletAudioClipFactory.class
deleted file mode 100644
index daba9b5..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/AppletAudioClipFactory.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/AppletImageFactory$1.class b/libraries/plugin/sun/plugin/viewer/context/AppletImageFactory$1.class
deleted file mode 100644
index 657bdc5..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/AppletImageFactory$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/AppletImageFactory.class b/libraries/plugin/sun/plugin/viewer/context/AppletImageFactory.class
deleted file mode 100644
index 2f793a5..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/AppletImageFactory.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext$1.class b/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext$1.class
deleted file mode 100644
index e164338..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext$1.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext$2.class b/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext$2.class
deleted file mode 100644
index 66b66c1..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext$2.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext.class b/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext.class
deleted file mode 100644
index 04d50a0..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/DefaultPluginAppletContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/MNetscape6AppletContext.class b/libraries/plugin/sun/plugin/viewer/context/MNetscape6AppletContext.class
deleted file mode 100644
index c78da7d..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/MNetscape6AppletContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/NetscapeAppletContext.class b/libraries/plugin/sun/plugin/viewer/context/NetscapeAppletContext.class
deleted file mode 100644
index dc852cf..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/NetscapeAppletContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/PluginAppletContext.class b/libraries/plugin/sun/plugin/viewer/context/PluginAppletContext.class
deleted file mode 100644
index e6ca125..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/PluginAppletContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/PluginAudioClip.class b/libraries/plugin/sun/plugin/viewer/context/PluginAudioClip.class
deleted file mode 100644
index 019e74f..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/PluginAudioClip.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/context/PluginBeansContext.class b/libraries/plugin/sun/plugin/viewer/context/PluginBeansContext.class
deleted file mode 100644
index 8047583..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/context/PluginBeansContext.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/frame/MNetscapeEmbeddedFrame.class b/libraries/plugin/sun/plugin/viewer/frame/MNetscapeEmbeddedFrame.class
deleted file mode 100644
index 3716024..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/frame/MNetscapeEmbeddedFrame.class and /dev/null differ
diff --git a/libraries/plugin/sun/plugin/viewer/frame/XNetscapeEmbeddedFrame.class b/libraries/plugin/sun/plugin/viewer/frame/XNetscapeEmbeddedFrame.class
deleted file mode 100644
index 1db41c8..0000000
Binary files a/libraries/plugin/sun/plugin/viewer/frame/XNetscapeEmbeddedFrame.class and /dev/null differ
diff --git a/manifest.mf b/manifest.mf
deleted file mode 100644
index 328e8e5..0000000
--- a/manifest.mf
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-X-COMMENT: Main-Class will be added automatically by build
-
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
deleted file mode 100644
index bc8163c..0000000
--- a/nbproject/build-impl.xml
+++ /dev/null
@@ -1,894 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-*** GENERATED FROM project.xml - DO NOT EDIT  ***
-***         EDIT ../build.xml INSTEAD         ***
-
-For the purpose of easier reading the script
-is divided into following sections:
-
-  - initialization
-  - compilation
-  - jar
-  - execution
-  - debugging
-  - javadoc
-  - junit compilation
-  - junit execution
-  - junit debugging
-  - applet
-  - cleanup
-
-        -->
-<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="OpticalRayTracer-impl">
-    <fail message="Please build using Ant 1.7.1 or higher.">
-        <condition>
-            <not>
-                <antversion atleast="1.7.1"/>
-            </not>
-        </condition>
-    </fail>
-    <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
-    <!-- 
-                ======================
-                INITIALIZATION SECTION 
-                ======================
-            -->
-    <target name="-pre-init">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="-pre-init" name="-init-private">
-        <property file="nbproject/private/config.properties"/>
-        <property file="nbproject/private/configs/${config}.properties"/>
-        <property file="nbproject/private/private.properties"/>
-    </target>
-    <target depends="-pre-init,-init-private" name="-init-user">
-        <property file="${user.properties.file}"/>
-        <!-- The two properties below are usually overridden -->
-        <!-- by the active platform. Just a fallback. -->
-        <property name="default.javac.source" value="1.4"/>
-        <property name="default.javac.target" value="1.4"/>
-    </target>
-    <target depends="-pre-init,-init-private,-init-user" name="-init-project">
-        <property file="nbproject/configs/${config}.properties"/>
-        <property file="nbproject/project.properties"/>
-    </target>
-    <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
-        <available file="${manifest.file}" property="manifest.available"/>
-        <available file="${application.splash}" property="splashscreen.available"/>
-        <condition property="main.class.available">
-            <and>
-                <isset property="main.class"/>
-                <not>
-                    <equals arg1="${main.class}" arg2="" trim="true"/>
-                </not>
-            </and>
-        </condition>
-        <condition property="manifest.available+main.class">
-            <and>
-                <isset property="manifest.available"/>
-                <isset property="main.class.available"/>
-            </and>
-        </condition>
-        <condition property="do.mkdist">
-            <and>
-                <isset property="libs.CopyLibs.classpath"/>
-                <not>
-                    <istrue value="${mkdist.disabled}"/>
-                </not>
-            </and>
-        </condition>
-        <condition property="manifest.available+main.class+mkdist.available">
-            <and>
-                <istrue value="${manifest.available+main.class}"/>
-                <isset property="do.mkdist"/>
-            </and>
-        </condition>
-        <condition property="manifest.available+main.class+mkdist.available+splashscreen.available">
-            <and>
-                <istrue value="${manifest.available+main.class+mkdist.available}"/>
-                <istrue value="${splashscreen.available}"/>
-            </and>
-        </condition>
-        <condition property="do.archive">
-            <not>
-                <istrue value="${jar.archive.disabled}"/>
-            </not>
-        </condition>
-        <condition property="do.archive+manifest.available">
-            <and>
-                <isset property="manifest.available"/>
-                <istrue value="${do.archive}"/>
-            </and>
-        </condition>
-        <condition property="do.archive+manifest.available+main.class">
-            <and>
-                <istrue value="${manifest.available+main.class}"/>
-                <istrue value="${do.archive}"/>
-            </and>
-        </condition>
-        <condition property="do.archive+manifest.available+main.class+mkdist.available">
-            <and>
-                <istrue value="${manifest.available+main.class+mkdist.available}"/>
-                <istrue value="${do.archive}"/>
-            </and>
-        </condition>
-        <condition property="do.archive+manifest.available+main.class+mkdist.available+splashscreen.available">
-            <and>
-                <istrue value="${manifest.available+main.class+mkdist.available+splashscreen.available}"/>
-                <istrue value="${do.archive}"/>
-            </and>
-        </condition>
-        <condition property="have.tests">
-            <or>
-                <available file="${test.src.dir}"/>
-            </or>
-        </condition>
-        <condition property="have.sources">
-            <or>
-                <available file="${src.dir}"/>
-            </or>
-        </condition>
-        <condition property="netbeans.home+have.tests">
-            <and>
-                <isset property="netbeans.home"/>
-                <isset property="have.tests"/>
-            </and>
-        </condition>
-        <condition property="no.javadoc.preview">
-            <and>
-                <isset property="javadoc.preview"/>
-                <isfalse value="${javadoc.preview}"/>
-            </and>
-        </condition>
-        <property name="run.jvmargs" value=""/>
-        <property name="javac.compilerargs" value=""/>
-        <property name="work.dir" value="${basedir}"/>
-        <condition property="no.deps">
-            <and>
-                <istrue value="${no.dependencies}"/>
-            </and>
-        </condition>
-        <property name="javac.debug" value="true"/>
-        <property name="javadoc.preview" value="true"/>
-        <property name="application.args" value=""/>
-        <property name="source.encoding" value="${file.encoding}"/>
-        <property name="runtime.encoding" value="${source.encoding}"/>
-        <condition property="javadoc.encoding.used" value="${javadoc.encoding}">
-            <and>
-                <isset property="javadoc.encoding"/>
-                <not>
-                    <equals arg1="${javadoc.encoding}" arg2=""/>
-                </not>
-            </and>
-        </condition>
-        <property name="javadoc.encoding.used" value="${source.encoding}"/>
-        <property name="includes" value="**"/>
-        <property name="excludes" value=""/>
-        <property name="do.depend" value="false"/>
-        <condition property="do.depend.true">
-            <istrue value="${do.depend}"/>
-        </condition>
-        <path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
-        <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
-            <length length="0" string="${endorsed.classpath}" when="greater"/>
-        </condition>
-        <property name="javac.fork" value="false"/>
-        <property name="jar.index" value="false"/>
-        <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
-    </target>
-    <target name="-post-init">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
-        <fail unless="src.dir">Must set src.dir</fail>
-        <fail unless="test.src.dir">Must set test.src.dir</fail>
-        <fail unless="build.dir">Must set build.dir</fail>
-        <fail unless="dist.dir">Must set dist.dir</fail>
-        <fail unless="build.classes.dir">Must set build.classes.dir</fail>
-        <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
-        <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
-        <fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
-        <fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
-        <fail unless="dist.jar">Must set dist.jar</fail>
-    </target>
-    <target name="-init-macrodef-property">
-        <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
-            <attribute name="name"/>
-            <attribute name="value"/>
-            <sequential>
-                <property name="@{name}" value="${@{value}}"/>
-            </sequential>
-        </macrodef>
-    </target>
-    <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors">
-        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${src.dir}" name="srcdir"/>
-            <attribute default="${build.classes.dir}" name="destdir"/>
-            <attribute default="${javac.classpath}" name="classpath"/>
-            <attribute default="${javac.processorpath}" name="processorpath"/>
-            <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
-            <attribute default="${includes}" name="includes"/>
-            <attribute default="${excludes}" name="excludes"/>
-            <attribute default="${javac.debug}" name="debug"/>
-            <attribute default="${empty.dir}" name="sourcepath"/>
-            <attribute default="${empty.dir}" name="gensrcdir"/>
-            <element name="customize" optional="true"/>
-            <sequential>
-                <property location="${build.dir}/empty" name="empty.dir"/>
-                <mkdir dir="${empty.dir}"/>
-                <mkdir dir="@{apgeneratedsrcdir}"/>
-                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
-                    <src>
-                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
-                            <include name="*"/>
-                        </dirset>
-                    </src>
-                    <classpath>
-                        <path path="@{classpath}"/>
-                    </classpath>
-                    <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
-                    <compilerarg line="${javac.compilerargs}"/>
-                    <compilerarg value="-processorpath"/>
-                    <compilerarg path="@{processorpath}:${empty.dir}"/>
-                    <compilerarg line="${ap.processors.internal}"/>
-                    <compilerarg line="${annotation.processing.processor.options}"/>
-                    <compilerarg value="-s"/>
-                    <compilerarg path="@{apgeneratedsrcdir}"/>
-                    <compilerarg line="${ap.proc.none.internal}"/>
-                    <customize/>
-                </javac>
-            </sequential>
-        </macrodef>
-    </target>
-    <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal">
-        <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${src.dir}" name="srcdir"/>
-            <attribute default="${build.classes.dir}" name="destdir"/>
-            <attribute default="${javac.classpath}" name="classpath"/>
-            <attribute default="${javac.processorpath}" name="processorpath"/>
-            <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
-            <attribute default="${includes}" name="includes"/>
-            <attribute default="${excludes}" name="excludes"/>
-            <attribute default="${javac.debug}" name="debug"/>
-            <attribute default="${empty.dir}" name="sourcepath"/>
-            <attribute default="${empty.dir}" name="gensrcdir"/>
-            <element name="customize" optional="true"/>
-            <sequential>
-                <property location="${build.dir}/empty" name="empty.dir"/>
-                <mkdir dir="${empty.dir}"/>
-                <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}">
-                    <src>
-                        <dirset dir="@{gensrcdir}" erroronmissingdir="false">
-                            <include name="*"/>
-                        </dirset>
-                    </src>
-                    <classpath>
-                        <path path="@{classpath}"/>
-                    </classpath>
-                    <compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
-                    <compilerarg line="${javac.compilerargs}"/>
-                    <customize/>
-                </javac>
-            </sequential>
-        </macrodef>
-    </target>
-    <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac">
-        <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${src.dir}" name="srcdir"/>
-            <attribute default="${build.classes.dir}" name="destdir"/>
-            <attribute default="${javac.classpath}" name="classpath"/>
-            <sequential>
-                <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
-                    <classpath>
-                        <path path="@{classpath}"/>
-                    </classpath>
-                </depend>
-            </sequential>
-        </macrodef>
-        <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${build.classes.dir}" name="destdir"/>
-            <sequential>
-                <fail unless="javac.includes">Must set javac.includes</fail>
-                <pathconvert pathsep="${line.separator}" property="javac.includes.binary">
-                    <path>
-                        <filelist dir="@{destdir}" files="${javac.includes}"/>
-                    </path>
-                    <globmapper from="*.java" to="*.class"/>
-                </pathconvert>
-                <tempfile deleteonexit="true" property="javac.includesfile.binary"/>
-                <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/>
-                <delete>
-                    <files includesfile="${javac.includesfile.binary}"/>
-                </delete>
-                <delete file="${javac.includesfile.binary}"/>
-            </sequential>
-        </macrodef>
-    </target>
-    <target name="-init-macrodef-junit">
-        <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${includes}" name="includes"/>
-            <attribute default="${excludes}" name="excludes"/>
-            <attribute default="**" name="testincludes"/>
-            <sequential>
-                <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true" tempdir="${build.dir}">
-                    <batchtest todir="${build.test.results.dir}">
-                        <fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
-                            <filename name="@{testincludes}"/>
-                        </fileset>
-                    </batchtest>
-                    <classpath>
-                        <path path="${run.test.classpath}"/>
-                    </classpath>
-                    <syspropertyset>
-                        <propertyref prefix="test-sys-prop."/>
-                        <mapper from="test-sys-prop.*" to="*" type="glob"/>
-                    </syspropertyset>
-                    <formatter type="brief" usefile="false"/>
-                    <formatter type="xml"/>
-                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
-                    <jvmarg line="${run.jvmargs}"/>
-                </junit>
-            </sequential>
-        </macrodef>
-    </target>
-    <target depends="-init-debug-args" name="-init-macrodef-nbjpda">
-        <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
-            <attribute default="${main.class}" name="name"/>
-            <attribute default="${debug.classpath}" name="classpath"/>
-            <attribute default="" name="stopclassname"/>
-            <sequential>
-                <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
-                    <classpath>
-                        <path path="@{classpath}"/>
-                    </classpath>
-                </nbjpdastart>
-            </sequential>
-        </macrodef>
-        <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
-            <attribute default="${build.classes.dir}" name="dir"/>
-            <sequential>
-                <nbjpdareload>
-                    <fileset dir="@{dir}" includes="${fix.classes}">
-                        <include name="${fix.includes}*.class"/>
-                    </fileset>
-                </nbjpdareload>
-            </sequential>
-        </macrodef>
-    </target>
-    <target name="-init-debug-args">
-        <property name="version-output" value="java version "${ant.java.version}"/>
-        <condition property="have-jdk-older-than-1.4">
-            <or>
-                <contains string="${version-output}" substring="java version "1.0"/>
-                <contains string="${version-output}" substring="java version "1.1"/>
-                <contains string="${version-output}" substring="java version "1.2"/>
-                <contains string="${version-output}" substring="java version "1.3"/>
-            </or>
-        </condition>
-        <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
-            <istrue value="${have-jdk-older-than-1.4}"/>
-        </condition>
-        <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
-            <os family="windows"/>
-        </condition>
-        <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
-            <isset property="debug.transport"/>
-        </condition>
-    </target>
-    <target depends="-init-debug-args" name="-init-macrodef-debug">
-        <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <attribute default="${main.class}" name="classname"/>
-            <attribute default="${debug.classpath}" name="classpath"/>
-            <element name="customize" optional="true"/>
-            <sequential>
-                <java classname="@{classname}" dir="${work.dir}" fork="true">
-                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
-                    <jvmarg line="${debug-args-line}"/>
-                    <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
-                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
-                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
-                    <jvmarg line="${run.jvmargs}"/>
-                    <classpath>
-                        <path path="@{classpath}"/>
-                    </classpath>
-                    <syspropertyset>
-                        <propertyref prefix="run-sys-prop."/>
-                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
-                    </syspropertyset>
-                    <customize/>
-                </java>
-            </sequential>
-        </macrodef>
-    </target>
-    <target name="-init-macrodef-java">
-        <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
-            <attribute default="${main.class}" name="classname"/>
-            <attribute default="${run.classpath}" name="classpath"/>
-            <element name="customize" optional="true"/>
-            <sequential>
-                <java classname="@{classname}" dir="${work.dir}" fork="true">
-                    <jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
-                    <jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
-                    <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
-                    <jvmarg line="${run.jvmargs}"/>
-                    <classpath>
-                        <path path="@{classpath}"/>
-                    </classpath>
-                    <syspropertyset>
-                        <propertyref prefix="run-sys-prop."/>
-                        <mapper from="run-sys-prop.*" to="*" type="glob"/>
-                    </syspropertyset>
-                    <customize/>
-                </java>
-            </sequential>
-        </macrodef>
-    </target>
-    <target name="-init-macrodef-copylibs">
-        <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3">
-            <element name="customize" optional="true"/>
-            <sequential>
-                <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
-                <pathconvert property="run.classpath.without.build.classes.dir">
-                    <path path="${run.classpath}"/>
-                    <map from="${build.classes.dir.resolved}" to=""/>
-                </pathconvert>
-                <pathconvert pathsep=" " property="jar.classpath">
-                    <path path="${run.classpath.without.build.classes.dir}"/>
-                    <chainedmapper>
-                        <flattenmapper/>
-                        <globmapper from="*" to="lib/*"/>
-                    </chainedmapper>
-                </pathconvert>
-                <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
-                <copylibs compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
-                    <fileset dir="${build.classes.dir}"/>
-                    <manifest>
-                        <attribute name="Class-Path" value="${jar.classpath}"/>
-                        <customize/>
-                    </manifest>
-                </copylibs>
-            </sequential>
-        </macrodef>
-    </target>
-    <target name="-init-presetdef-jar">
-        <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
-            <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}">
-                <j2seproject1:fileset dir="${build.classes.dir}"/>
-            </jar>
-        </presetdef>
-    </target>
-    <target name="-init-ap-cmdline-properties">
-        <property name="annotation.processing.enabled" value="true"/>
-        <property name="annotation.processing.processors.list" value=""/>
-        <property name="annotation.processing.processor.options" value=""/>
-        <property name="annotation.processing.run.all.processors" value="true"/>
-        <property name="javac.processorpath" value="${javac.classpath}"/>
-        <property name="javac.test.processorpath" value="${javac.test.classpath}"/>
-        <condition property="ap.supported.internal" value="true">
-            <not>
-                <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/>
-            </not>
-        </condition>
-    </target>
-    <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported">
-        <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}">
-            <isfalse value="${annotation.processing.run.all.processors}"/>
-        </condition>
-        <condition else="" property="ap.proc.none.internal" value="-proc:none">
-            <isfalse value="${annotation.processing.enabled}"/>
-        </condition>
-    </target>
-    <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
-        <property name="ap.cmd.line.internal" value=""/>
-    </target>
-    <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
-    <!--
-                ===================
-                COMPILATION SECTION
-                ===================
-            -->
-    <target name="-deps-jar-init" unless="built-jar.properties">
-        <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/>
-        <delete file="${built-jar.properties}" quiet="true"/>
-    </target>
-    <target if="already.built.jar.${basedir}" name="-warn-already-built-jar">
-        <echo level="warn" message="Cycle detected: OpticalRayTracer was already built"/>
-    </target>
-    <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps">
-        <mkdir dir="${build.dir}"/>
-        <touch file="${built-jar.properties}" verbose="false"/>
-        <property file="${built-jar.properties}" prefix="already.built.jar."/>
-        <antcall target="-warn-already-built-jar"/>
-        <propertyfile file="${built-jar.properties}">
-            <entry key="${basedir}" value=""/>
-        </propertyfile>
-    </target>
-    <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
-    <target depends="init" name="-check-automatic-build">
-        <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
-    </target>
-    <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
-        <antcall target="clean"/>
-    </target>
-    <target depends="init,deps-jar" name="-pre-pre-compile">
-        <mkdir dir="${build.classes.dir}"/>
-    </target>
-    <target name="-pre-compile">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target if="do.depend.true" name="-compile-depend">
-        <pathconvert property="build.generated.subdirs">
-            <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false">
-                <include name="*"/>
-            </dirset>
-        </pathconvert>
-        <j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/>
-    </target>
-    <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile">
-        <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/>
-        <copy todir="${build.classes.dir}">
-            <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
-        </copy>
-    </target>
-    <target if="has.persistence.xml" name="-copy-persistence-xml">
-        <mkdir dir="${build.classes.dir}/META-INF"/>
-        <copy todir="${build.classes.dir}/META-INF">
-            <fileset dir="${meta.inf.dir}" includes="persistence.xml"/>
-        </copy>
-    </target>
-    <target name="-post-compile">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
-    <target name="-pre-compile-single">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
-        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
-        <j2seproject3:force-recompile/>
-        <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/>
-    </target>
-    <target name="-post-compile-single">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
-    <!--
-                ====================
-                JAR BUILDING SECTION
-                ====================
-            -->
-    <target depends="init" name="-pre-pre-jar">
-        <dirname file="${dist.jar}" property="dist.jar.dir"/>
-        <mkdir dir="${dist.jar.dir}"/>
-    </target>
-    <target name="-pre-jar">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive" name="-do-jar-without-manifest" unless="manifest.available">
-        <j2seproject1:jar/>
-    </target>
-    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
-        <j2seproject1:jar manifest="${manifest.file}"/>
-    </target>
-    <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
-        <j2seproject1:jar manifest="${manifest.file}">
-            <j2seproject1:manifest>
-                <j2seproject1:attribute name="Main-Class" value="${main.class}"/>
-            </j2seproject1:manifest>
-        </j2seproject1:jar>
-        <echo>To run this application from the command line without Ant, try:</echo>
-        <property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
-        <property location="${dist.jar}" name="dist.jar.resolved"/>
-        <pathconvert property="run.classpath.with.dist.jar">
-            <path path="${run.classpath}"/>
-            <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
-        </pathconvert>
-        <echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
-    </target>
-    <target depends="init,compile,-pre-pre-jar,-pre-jar,-init-macrodef-copylibs" if="do.archive+manifest.available+main.class+mkdist.available+splashscreen.available" name="-do-jar-with-libraries-and-splashscreen">
-        <basename file="${application.splash}" property="splashscreen.basename"/>
-        <mkdir dir="${build.classes.dir}/META-INF"/>
-        <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
-        <j2seproject3:copylibs>
-            <customize>
-                <attribute name="Main-Class" value="${main.class}"/>
-                <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
-            </customize>
-        </j2seproject3:copylibs>
-        <echo>To run this application from the command line without Ant, try:</echo>
-        <property location="${dist.jar}" name="dist.jar.resolved"/>
-        <echo>java -jar "${dist.jar.resolved}"</echo>
-    </target>
-    <target depends="init,compile,-pre-pre-jar,-pre-jar,-init-macrodef-copylibs" if="do.archive+manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries" unless="splashscreen.available">
-        <j2seproject3:copylibs>
-            <customize>
-                <attribute name="Main-Class" value="${main.class}"/>
-            </customize>
-        </j2seproject3:copylibs>
-        <echo>To run this application from the command line without Ant, try:</echo>
-        <property location="${dist.jar}" name="dist.jar.resolved"/>
-        <echo>java -jar "${dist.jar.resolved}"</echo>
-    </target>
-    <target name="-post-jar">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries-and-splashscreen,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
-    <!--
-                =================
-                EXECUTION SECTION
-                =================
-            -->
-    <target depends="init,compile" description="Run a main class." name="run">
-        <j2seproject1:java>
-            <customize>
-                <arg line="${application.args}"/>
-            </customize>
-        </j2seproject1:java>
-    </target>
-    <target name="-do-not-recompile">
-        <property name="javac.includes.binary" value=""/>
-    </target>
-    <target depends="init,compile-single" name="run-single">
-        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
-        <j2seproject1:java classname="${run.class}"/>
-    </target>
-    <target depends="init,compile-test-single" name="run-test-with-main">
-        <fail unless="run.class">Must select one file in the IDE or set run.class</fail>
-        <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/>
-    </target>
-    <!--
-                =================
-                DEBUGGING SECTION
-                =================
-            -->
-    <target depends="init" if="netbeans.home" name="-debug-start-debugger">
-        <j2seproject1:nbjpdastart name="${debug.class}"/>
-    </target>
-    <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test">
-        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/>
-    </target>
-    <target depends="init,compile" name="-debug-start-debuggee">
-        <j2seproject3:debug>
-            <customize>
-                <arg line="${application.args}"/>
-            </customize>
-        </j2seproject3:debug>
-    </target>
-    <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
-    <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
-        <j2seproject1:nbjpdastart stopclassname="${main.class}"/>
-    </target>
-    <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
-    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
-        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
-        <j2seproject3:debug classname="${debug.class}"/>
-    </target>
-    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
-    <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test">
-        <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
-        <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/>
-    </target>
-    <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/>
-    <target depends="init" name="-pre-debug-fix">
-        <fail unless="fix.includes">Must set fix.includes</fail>
-        <property name="javac.includes" value="${fix.includes}.java"/>
-    </target>
-    <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
-        <j2seproject1:nbjpdareload/>
-    </target>
-    <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
-    <!--
-                ===============
-                JAVADOC SECTION
-                ===============
-            -->
-    <target depends="init" if="have.sources" name="-javadoc-build">
-        <mkdir dir="${dist.javadoc.dir}"/>
-        <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
-            <classpath>
-                <path path="${javac.classpath}"/>
-            </classpath>
-            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
-                <filename name="**/*.java"/>
-            </fileset>
-            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
-                <include name="**/*.java"/>
-            </fileset>
-        </javadoc>
-        <copy todir="${dist.javadoc.dir}">
-            <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
-                <filename name="**/doc-files/**"/>
-            </fileset>
-            <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
-                <include name="**/doc-files/**"/>
-            </fileset>
-        </copy>
-    </target>
-    <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
-        <nbbrowse file="${dist.javadoc.dir}/index.html"/>
-    </target>
-    <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
-    <!--
-                =========================
-                JUNIT COMPILATION SECTION
-                =========================
-            -->
-    <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
-        <mkdir dir="${build.test.classes.dir}"/>
-    </target>
-    <target name="-pre-compile-test">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target if="do.depend.true" name="-compile-test-depend">
-        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
-    </target>
-    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
-        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir="${test.src.dir}"/>
-        <copy todir="${build.test.classes.dir}">
-            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
-        </copy>
-    </target>
-    <target name="-post-compile-test">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
-    <target name="-pre-compile-test-single">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
-        <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
-        <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
-        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
-        <copy todir="${build.test.classes.dir}">
-            <fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
-        </copy>
-    </target>
-    <target name="-post-compile-test-single">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
-    <!--
-                =======================
-                JUNIT EXECUTION SECTION
-                =======================
-            -->
-    <target depends="init" if="have.tests" name="-pre-test-run">
-        <mkdir dir="${build.test.results.dir}"/>
-    </target>
-    <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
-        <j2seproject3:junit testincludes="**/*Test.java"/>
-    </target>
-    <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
-        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
-    </target>
-    <target depends="init" if="have.tests" name="test-report"/>
-    <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
-    <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
-    <target depends="init" if="have.tests" name="-pre-test-run-single">
-        <mkdir dir="${build.test.results.dir}"/>
-    </target>
-    <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
-        <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
-        <j2seproject3:junit excludes="" includes="${test.includes}"/>
-    </target>
-    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
-        <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
-    </target>
-    <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
-    <!--
-                =======================
-                JUNIT DEBUGGING SECTION
-                =======================
-            -->
-    <target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
-        <fail unless="test.class">Must select one file in the IDE or set test.class</fail>
-        <property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
-        <delete file="${test.report.file}"/>
-        <mkdir dir="${build.test.results.dir}"/>
-        <j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
-            <customize>
-                <syspropertyset>
-                    <propertyref prefix="test-sys-prop."/>
-                    <mapper from="test-sys-prop.*" to="*" type="glob"/>
-                </syspropertyset>
-                <arg value="${test.class}"/>
-                <arg value="showoutput=true"/>
-                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
-                <arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
-            </customize>
-        </j2seproject3:debug>
-    </target>
-    <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
-        <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
-    </target>
-    <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
-    <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
-        <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
-    </target>
-    <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
-    <!--
-                =========================
-                APPLET EXECUTION SECTION
-                =========================
-            -->
-    <target depends="init,compile-single" name="run-applet">
-        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
-        <j2seproject1:java classname="sun.applet.AppletViewer">
-            <customize>
-                <arg value="${applet.url}"/>
-            </customize>
-        </j2seproject1:java>
-    </target>
-    <!--
-                =========================
-                APPLET DEBUGGING  SECTION
-                =========================
-            -->
-    <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
-        <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
-        <j2seproject3:debug classname="sun.applet.AppletViewer">
-            <customize>
-                <arg value="${applet.url}"/>
-            </customize>
-        </j2seproject3:debug>
-    </target>
-    <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
-    <!--
-                ===============
-                CLEANUP SECTION
-                ===============
-            -->
-    <target name="-deps-clean-init" unless="built-clean.properties">
-        <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/>
-        <delete file="${built-clean.properties}" quiet="true"/>
-    </target>
-    <target if="already.built.clean.${basedir}" name="-warn-already-built-clean">
-        <echo level="warn" message="Cycle detected: OpticalRayTracer was already built"/>
-    </target>
-    <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps">
-        <mkdir dir="${build.dir}"/>
-        <touch file="${built-clean.properties}" verbose="false"/>
-        <property file="${built-clean.properties}" prefix="already.built.clean."/>
-        <antcall target="-warn-already-built-clean"/>
-        <propertyfile file="${built-clean.properties}">
-            <entry key="${basedir}" value=""/>
-        </propertyfile>
-    </target>
-    <target depends="init" name="-do-clean">
-        <delete dir="${build.dir}"/>
-        <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/>
-    </target>
-    <target name="-post-clean">
-        <!-- Empty placeholder for easier customization. -->
-        <!-- You can override this target in the ../build.xml file. -->
-    </target>
-    <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
-    <target name="-check-call-dep">
-        <property file="${call.built.properties}" prefix="already.built."/>
-        <condition property="should.call.dep">
-            <not>
-                <isset property="already.built.${call.subproject}"/>
-            </not>
-        </condition>
-    </target>
-    <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">
-        <ant antfile="${call.script}" inheritall="false" target="${call.target}">
-            <propertyset>
-                <propertyref prefix="transfer."/>
-                <mapper from="transfer.*" to="*" type="glob"/>
-            </propertyset>
-        </ant>
-    </target>
-</project>
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
deleted file mode 100644
index bd33833..0000000
--- a/nbproject/genfiles.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-build.xml.data.CRC32=bb1b73d1
-build.xml.script.CRC32=9bd1f386
-build.xml.stylesheet.CRC32=28e38971 at 1.38.3.45
-# 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=bb1b73d1
-nbproject/build-impl.xml.script.CRC32=d4ae2d0b
-nbproject/build-impl.xml.stylesheet.CRC32=229523de at 1.38.3.45
diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties
deleted file mode 100644
index e69de29..0000000
diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties
deleted file mode 100644
index 03e945f..0000000
--- a/nbproject/private/private.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-compile.on.save=true
-do.depend=false
-do.jar=true
-javac.debug=true
-javadoc.preview=true
-user.properties.file=/home/lutusp/.netbeans/6.9/build.properties
diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml
deleted file mode 100644
index 49d56cf..0000000
--- a/nbproject/private/private.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
-    <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
-    <open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/1">
-        <file>file:/netbackup/data/java2/OpticalRayTracer/src/opticalraytracer/OpticalRayTracerApp.java</file>
-        <file>file:/netbackup/data/java2/OpticalRayTracer/src/opticalraytracer/RayTraceComputer.java</file>
-        <file>file:/netbackup/data/java2/OpticalRayTracer/src/opticalraytracer/OpticalRayTracer.java</file>
-        <file>file:/netbackup/data/java2/OpticalRayTracer/src/opticalraytracer/OpticalRayTracerApplet.java</file>
-    </open-files>
-</project-private>
diff --git a/nbproject/project.properties b/nbproject/project.properties
deleted file mode 100644
index eca1933..0000000
--- a/nbproject/project.properties
+++ /dev/null
@@ -1,81 +0,0 @@
-annotation.processing.enabled=true
-annotation.processing.enabled.in.editor=false
-annotation.processing.run.all.processors=true
-application.title=OpticalRayTracer
-application.vendor=lutusp
-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}
-# This directory is removed when the project is cleaned:
-dist.dir=dist
-dist.jar=${dist.dir}/OpticalRayTracer.jar
-dist.javadoc.dir=${dist.dir}/javadoc
-endorsed.classpath=
-excludes=
-file.reference.OpticalRayTracer-extras=extras
-file.reference.plugin.jar=libraries/plugin.jar
-includes=**
-jar.archive.disabled=${jnlp.enabled}
-jar.compress=true
-jar.index=${jnlp.enabled}
-javac.classpath=\
-    ${file.reference.plugin.jar}
-# Space-separated list of extra javac options
-javac.compilerargs=-Xlint:unchecked
-javac.deprecation=false
-javac.processorpath=\
-    ${javac.classpath}:\
-    ${file.reference.OpticalRayTracer-extras}
-javac.source=1.5
-javac.target=1.5
-javac.test.classpath=\
-    ${javac.classpath}:\
-    ${build.classes.dir}:\
-    ${libs.junit.classpath}:\
-    ${libs.junit_4.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=
-jnlp.applet.class=opticalraytracer.OpticalRayTracerApplet
-jnlp.applet.height=300
-jnlp.applet.width=300
-jnlp.codebase.type=no.codebase
-jnlp.descriptor=application
-jnlp.enabled=false
-jnlp.mixed.code=defaut
-jnlp.offline-allowed=false
-jnlp.signed=false
-main.class=opticalraytracer.OpticalRayTracerApp
-manifest.file=manifest.mf
-meta.inf.dir=${src.dir}/META-INF
-platform.active=default_platform
-run.classpath=\
-    ${javac.classpath}:\
-    ${build.classes.dir}
-run.test.classpath=\
-    ${javac.test.classpath}:\
-    ${build.test.classes.dir}
-source.encoding=UTF-8
-src.dir=src
-test.src.dir=test
diff --git a/nbproject/project.xml b/nbproject/project.xml
deleted file mode 100644
index 6cfcab4..0000000
--- a/nbproject/project.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://www.netbeans.org/ns/project/1">
-    <type>org.netbeans.modules.java.j2seproject</type>
-    <configuration>
-        <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
-            <name>OpticalRayTracer</name>
-            <minimum-ant-version>1.6.5</minimum-ant-version>
-            <source-roots>
-                <root id="src.dir"/>
-            </source-roots>
-            <test-roots>
-                <root id="test.src.dir"/>
-            </test-roots>
-        </data>
-    </configuration>
-</project>
diff --git a/src/net/miginfocom/layout/AC.java b/src/net/miginfocom/layout/AC.java
new file mode 100644
index 0000000..a366f17
--- /dev/null
+++ b/src/net/miginfocom/layout/AC.java
@@ -0,0 +1,564 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A constraint that holds the column <b>or</b> row constraints for the grid. It also holds the gaps between the rows and columns.
+ * <p>
+ * This class is a holder and builder for a number of {@link net.miginfocom.layout.DimConstraint}s.
+ * <p>
+ * For a more thorough explanation of what these constraints do, and how to build the constraints, see the White Paper or Cheat Sheet at www.migcomponents.com.
+ * <p>
+ * Note that there are two way to build this constraint. Through String (e.g. <code>"[100]3[200,fill]"</code> or through API (E.g.
+ * <code>new AxisConstraint().size("100").gap("3").size("200").fill()</code>.
+ */
+public final class AC implements Externalizable
+{
+	private final ArrayList<DimConstraint> cList = new ArrayList<DimConstraint>(8);
+
+	private transient int curIx = 0;
+
+	/** Constructor. Creates an instance that can be configured manually. Will be initialized with a default
+	 * {@link net.miginfocom.layout.DimConstraint}.
+	 */
+	public AC()
+	{
+		cList.add(new DimConstraint());
+	}
+
+	/** Property. The different {@link net.miginfocom.layout.DimConstraint}s that this object consists of.
+	 * These <code><DimConstraints/code> contains all information in this class.
+	 * <p>
+	 * Yes, we are embarrassingly aware that the method is misspelled.
+	 * @return The different {@link net.miginfocom.layout.DimConstraint}s that this object consists of. A new list and
+	 * never <code>null</code>.
+	 */
+	public final DimConstraint[] getConstaints()
+	{
+		return cList.toArray(new DimConstraint[cList.size()]);
+	}
+
+	/** Sets the different {@link net.miginfocom.layout.DimConstraint}s that this object should consists of.
+	 * <p>
+	 * Yes, we are embarrassingly aware that the method is misspelled.
+	 * @param constr The different {@link net.miginfocom.layout.DimConstraint}s that this object consists of. The list
+	 * will be copied for storage. <code>null</code> or and emty array will reset the constraints to one <code>DimConstraint</code>
+	 * with default values.
+	 */
+	public final void setConstaints(DimConstraint[] constr)
+	{
+		if (constr == null || constr.length < 1 )
+			constr = new DimConstraint[] {new DimConstraint()};
+
+		cList.clear();
+		cList.ensureCapacity(constr.length);
+		for (DimConstraint c : constr)
+			cList.add(c);
+	}
+
+	/** Returns the number of rows/columns that this constraints currently have.
+	 * @return The number of rows/columns that this constraints currently have. At least 1.
+	 */
+	public int getCount()
+	{
+		return cList.size();
+	}
+
+	/** Sets the total number of rows/columns to <code>size</code>. If the number of rows/columns is already more
+	 * than <code>size</code> nothing will happen.
+	 * @param size The total number of rows/columns
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC count(int size)
+	{
+		makeSize(size);
+		return this;
+	}
+
+	/** Specifies that the current row/column should not be grid-like. The while row/colum will have its components layed out
+	 * in one single cell. It is the same as to say that the cells in this column/row will all be merged (a.k.a spanned).
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC noGrid()
+	{
+		return noGrid(curIx);
+	}
+
+	/** Specifies that the indicated rows/columns should not be grid-like. The while row/colum will have its components layed out
+	 * in one single cell. It is the same as to say that the cells in this column/row will all be merged (a.k.a spanned).
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC noGrid(int... indexes)
+	{
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setNoGrid(true);
+		}
+		return this;
+	}
+
+	/** Sets the current row/column to <code>i</code>. If the current number of rows/columns is less than <code>i</code> a call
+	 * to {@link #count(int)} will set the size accordingly.
+	 * <p>
+	 * The next call to any of the constraint methods (e.g. {@link net.miginfocom.layout.AC#noGrid}) will be carried
+	 * out on this new row/column.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param i The new current row/column.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC index(int i)
+	{
+		makeSize(i);
+		curIx = i;
+		return this;
+	}
+
+	/** Specifies that the current row/column's component should grow by default. It does not affect the size of the row/column.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC fill()
+	{
+		return fill(curIx);
+	}
+
+	/** Specifies that the indicated rows'/columns' component should grow by default. It does not affect the size of the row/column.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC fill(int... indexes)
+	{
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setFill(true);
+		}
+		return this;
+	}
+
+//	/** Specifies that the current row/column should be put in the end group <code>s</code> and will thus share the same ending
+//	 * coordinate within the group.
+//	 * <p>
+//	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+//	 * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+//	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+//	 */
+//	public final AxisConstraint endGroup(String s)
+//	{
+//		return endGroup(s, curIx);
+//	}
+//
+//	/** Specifies that the indicated rows/columns should be put in the end group <code>s</code> and will thus share the same ending
+//	 * coordinate within the group.
+//	 * <p>
+//	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+//	 * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+//	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+//	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+//	 */
+//	public final AxisConstraint endGroup(String s, int... indexes)
+//	{
+//		for (int i = indexes.length - 1; i >= 0; i--) {
+//			int ix = indexes[i];
+//			makeSize(ix);
+//			cList.get(ix).setEndGroup(s);
+//		}
+//		return this;
+//	}
+
+	/** Specifies that the current row/column should be put in the size group <code>s</code> and will thus share the same size
+	 * constraints as the other components in the group.
+	 * <p>
+	 * Same as <code>sizeGroup("")</code>
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final AC sizeGroup()
+	{
+		return sizeGroup("", curIx);
+	}
+
+	/** Specifies that the current row/column should be put in the size group <code>s</code> and will thus share the same size
+	 * constraints as the other components in the group.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC sizeGroup(String s)
+	{
+		return sizeGroup(s, curIx);
+	}
+
+	/** Specifies that the indicated rows/columns should be put in the size group <code>s</code> and will thus share the same size
+	 * constraints as the other components in the group.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC sizeGroup(String s, int... indexes)
+	{
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setSizeGroup(s);
+		}
+		return this;
+	}
+
+	/** Specifies the current row/column's min and/or preferred and/or max size. E.g. <code>"10px"</code> or <code>"50:100:200"</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s The minimum and/or preferred and/or maximum size of this row. The string will be interpreted
+	 * as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC size(String s)
+	{
+		return size(s, curIx);
+	}
+
+	/** Specifies the indicated rows'/columns' min and/or preferred and/or max size. E.g. <code>"10px"</code> or <code>"50:100:200"</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The minimum and/or preferred and/or maximum size of this row. The string will be interpreted
+	 * as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC size(String size, int... indexes)
+	{
+		BoundSize bs = ConstraintParser.parseBoundSize(size, false, true);
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setSize(bs);
+		}
+		return this;
+	}
+
+	/** Specifies the gap size to be the default one <b>AND</b> moves to the next column/row. The method is called <code>.gap()</code>
+	 * rather the more natural <code>.next()</code> to indicate that it is very much related to the other <code>.gap(..)</code> methods.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC gap()
+	{
+		curIx++;
+		return this;
+	}
+
+	/** Specifies the gap size to <code>size</code> <b>AND</b> moves to the next column/row.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size minimum and/or preferred and/or maximum size of the gap between this and the next row/column.
+	 * The string will be interpreted as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC gap(String size)
+	{
+		return gap(size, curIx++);
+	}
+
+	/** Specifies the indicated rows'/columns' gap size to <code>size</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size minimum and/or preferred and/or maximum size of the gap between this and the next row/column.
+	 * The string will be interpreted as a <b>BoundSize</b>. For more info on how <b>BoundSize</b> is formatted see the documentation.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC gap(String size, int... indexes)
+	{
+		BoundSize bsa = size != null ? ConstraintParser.parseBoundSize(size, true, true) : null;
+
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			if (bsa != null)
+				cList.get(ix).setGapAfter(bsa);
+		}
+		return this;
+	}
+
+	/** Specifies the current row/column's columns default alignment <b>for its components</b>. It does not affect the positioning
+	 * or size of the columns/row itself. For columns it is the horizonal alignment (e.g. "left") and for rows it is the vertical
+	 * alignment (e.g. "top").
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param side The default side to align the components. E.g. "top" or "left", or "leading" or "trailing" or "bottom" or "right".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC align(String side)
+	{
+		return align(side, curIx);
+	}
+
+	/** Specifies the indicated rows'/columns' columns default alignment <b>for its components</b>. It does not affect the positioning
+	 * or size of the columns/row itself. For columns it is the horizonal alignment (e.g. "left") and for rows it is the vertical
+	 * alignment (e.g. "top").
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param side The default side to align the components. E.g. "top" or "left", or "before" or "after" or "bottom" or "right".
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC align(String side, int... indexes)
+	{
+		UnitValue al = ConstraintParser.parseAlignKeywords(side, true);
+		if (al == null)
+			al = ConstraintParser.parseAlignKeywords(side, false);
+
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setAlign(al);
+		}
+		return this;
+	}
+
+	/** Specifies the current row/column's grow priority.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The new grow priority.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC growPrio(int p)
+	{
+		return growPrio(p, curIx);
+	}
+
+	/** Specifies the indicated rows'/columns' grow priority.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The new grow priority.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC growPrio(int p, int... indexes)
+	{
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setGrowPriority(p);
+		}
+		return this;
+	}
+
+	/** Specifies the current row/column's grow weight within columns/rows with the <code>grow priority</code> 100f.
+	 * <p>
+	 * Same as <code>grow(100f)</code>
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final AC grow()
+	{
+		return grow(1f, curIx);
+	}
+
+	/** Specifies the current row/column's grow weight within columns/rows with the same <code>grow priority</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param w The new grow weight.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC grow(float w)
+	{
+		return grow(w, curIx);
+	}
+
+	/** Specifies the indicated rows'/columns' grow weight within columns/rows with the same <code>grow priority</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param w The new grow weight.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC grow(float w, int... indexes)
+	{
+		Float gw = new Float(w);
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setGrow(gw);
+		}
+		return this;
+	}
+
+	/** Specifies the current row/column's shrink priority.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The new shrink priority.
+	 	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC shrinkPrio(int p)
+	{
+		return shrinkPrio(p, curIx);
+	}
+
+	/** Specifies the indicated rows'/columns' shrink priority.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The new shrink priority.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final AC shrinkPrio(int p, int... indexes)
+	{
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setShrinkPriority(p);
+		}
+		return this;
+	}
+
+	/** Specifies that the current row/column's shrink weight withing the columns/rows with the <code>shrink priority</code> 100f.
+	 * <p>
+	 * Same as <code>shrink(100f)</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final AC shrink()
+	{
+		return shrink(100f, curIx);
+	}
+
+	/** Specifies that the current row/column's shrink weight withing the columns/rows with the same <code>shrink priority</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+	 * @param w The shrink weight.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final AC shrink(float w)
+	{
+		return shrink(w, curIx);
+	}
+
+	/** Specifies the indicated rows'/columns' shrink weight withing the columns/rows with the same <code>shrink priority</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+	 * @param w The shrink weight.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final AC shrink(float w, int... indexes)
+	{
+		Float sw = new Float(w);
+		for (int i = indexes.length - 1; i >= 0; i--) {
+			int ix = indexes[i];
+			makeSize(ix);
+			cList.get(ix).setShrink(sw);
+		}
+		return this;
+	}
+
+	/** Specifies that the current row/column's shrink weight withing the columns/rows with the same <code>shrink priority</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+	 * @param w The shrink weight.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 * @deprecated in 3.7.2. Use {@link #shrink(float)} instead.
+	 */
+	public final AC shrinkWeight(float w)
+	{
+		return shrink(w);
+	}
+
+	/** Specifies the indicated rows'/columns' shrink weight withing the columns/rows with the same <code>shrink priority</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the White Paper or Cheat Sheet at www.migcomponents.com.
+	 * @param w The shrink weight.
+	 * @param indexes The index(es) (0-based) of the columns/rows that should be affected by this constraint.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new AxisConstraint().noGrid().gap().fill()</code>.
+	 * @deprecated in 3.7.2. Use {@link #shrink(float, int...)} instead.
+	 */
+	public final AC shrinkWeight(float w, int... indexes)
+	{
+		return shrink(w, indexes);
+	}
+
+	private void makeSize(int sz)
+	{
+		if (cList.size() <= sz) {
+			cList.ensureCapacity(sz);
+			for (int i = cList.size(); i <= sz; i++)
+				cList.add(new DimConstraint());
+		}
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+
+	public void writeExternal(ObjectOutput out) throws IOException
+	{
+		if (getClass() == AC.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+}
\ No newline at end of file
diff --git a/src/net/miginfocom/layout/BoundSize.java b/src/net/miginfocom/layout/BoundSize.java
new file mode 100644
index 0000000..bad4631
--- /dev/null
+++ b/src/net/miginfocom/layout/BoundSize.java
@@ -0,0 +1,273 @@
+package net.miginfocom.layout;
+
+import java.beans.Encoder;
+import java.beans.Expression;
+import java.beans.PersistenceDelegate;
+import java.io.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A size that contains minimum, preferred and maximum size of type {@link UnitValue}.
+ * <p>
+ * This class is a simple value container and it is immutable.
+ * <p>
+ * If a size is missing (i.e., <code>null</code>) that boundary should be considered "not in use".
+ * <p>
+ * You can create a BoundSize from a String with the use of {@link ConstraintParser#parseBoundSize(String, boolean, boolean)}
+ */
+public class BoundSize implements Serializable
+{
+	public static final BoundSize NULL_SIZE = new BoundSize(null, null);
+	public static final BoundSize ZERO_PIXEL = new BoundSize(UnitValue.ZERO, "0px");
+
+	private final transient UnitValue min;
+	private final transient UnitValue pref;
+	private final transient UnitValue max;
+	private final transient boolean gapPush;
+
+	/** Constructor that use the same value for min/preferred/max size.
+	 * @param minMaxPref The value to use for min/preferred/max size.
+	 * @param createString The string used to create the BoundsSize.
+	 */
+	public BoundSize(UnitValue minMaxPref, String createString)
+	{
+		this(minMaxPref, minMaxPref, minMaxPref, createString);
+	}
+
+	/** Constructor. <b>This method is here for serilization only and should normally not be used. Use
+	 * {@link ConstraintParser#parseBoundSize(String, boolean, boolean)} instead.
+	 * @param min The minimum size. May be <code>null</code>.
+	 * @param preferred  The preferred size. May be <code>null</code>.
+	 * @param max  The maximum size. May be <code>null</code>.
+	 * @param createString The string used to create the BoundsSize.
+	 */
+	public BoundSize(UnitValue min, UnitValue preferred, UnitValue max, String createString)    // Bound to old delegate!!!!!
+	{
+		this(min, preferred, max, false, createString);
+	}
+
+	/** Constructor. <b>This method is here for serilization only and should normally not be used. Use
+	 * {@link ConstraintParser#parseBoundSize(String, boolean, boolean)} instead.
+	 * @param min The minimum size. May be <code>null</code>.
+	 * @param preferred  The preferred size. May be <code>null</code>.
+	 * @param max  The maximum size. May be <code>null</code>.
+	 * @param gapPush If the size should be hinted as "pushing" and thus want to occupy free space if no one else is claiming it.
+	 * @param createString The string used to create the BoundsSize.
+	 */
+	public BoundSize(UnitValue min, UnitValue preferred, UnitValue max, boolean gapPush, String createString)
+	{
+		this.min = min;
+		this.pref = preferred;
+		this.max = max;
+		this.gapPush = gapPush;
+
+		LayoutUtil.putCCString(this, createString);    // this escapes!!
+	}
+
+	/** Returns the minimum size as sent into the constructor.
+	 * @return The minimum size as sent into the constructor. May be <code>null</code>.
+	 */
+	public final UnitValue getMin()
+	{
+		return min;
+	}
+
+	/** Returns the preferred size as sent into the constructor.
+	 * @return The preferred size as sent into the constructor. May be <code>null</code>.
+	 */
+	public final UnitValue getPreferred()
+	{
+		return pref;
+	}
+
+	/** Returns the maximum size as sent into the constructor.
+	 * @return The maximum size as sent into the constructor. May be <code>null</code>.
+	 */
+	public final UnitValue getMax()
+	{
+		return max;
+	}
+
+	/** If the size should be hinted as "pushing" and thus want to occupy free space if noone else is claiming it.
+	 * @return The value.
+	 */
+	public boolean getGapPush()
+	{
+		return gapPush;
+	}
+
+	/** Returns if this bound size has no min, preferred and maximum size set (they are all <code>null</code>)
+	 * @return If unset.
+	 */
+	public boolean isUnset()
+	{
+		// Most common case by far is this == ZERO_PIXEL...
+		return this == ZERO_PIXEL || (pref == null && min == null && max == null && gapPush == false);
+	}
+
+	/** Makes sure that <code>size</code> is within min and max of this size.
+	 * @param size The size to constrain.
+	 * @param refValue The reference to use for relative sizes.
+	 * @param parent The parent container.
+	 * @return The size, constrained within min and max.
+	 */
+	public int constrain(int size, float refValue, ContainerWrapper parent)
+	{
+		if (max != null)
+			size = Math.min(size, max.getPixels(refValue, parent, parent));
+		if (min != null)
+			size = Math.max(size, min.getPixels(refValue, parent, parent));
+		return size;
+	}
+
+	/** Returns the minimum, preferred or maximum size for this bounded size.
+	 * @param sizeType The type. <code>LayoutUtil.MIN</code>, <code>LayoutUtil.PREF</code> or <code>LayoutUtil.MAX</code>.
+	 * @return
+	 */
+	final UnitValue getSize(int sizeType)
+	{
+		switch(sizeType) {
+			case LayoutUtil.MIN:
+				return min;
+			case LayoutUtil.PREF:
+				return pref;
+			case LayoutUtil.MAX:
+				return max;
+			default:
+				throw new IllegalArgumentException("Unknown size: " + sizeType);
+		}
+	}
+
+	/** Convert the bound sizes to pixels.
+	 * <p>
+	 * <code>null</code> bound sizes will be 0 for min and preferred and {@link net.miginfocom.layout.LayoutUtil#INF} for max.
+	 * @param refSize The reference size.
+	 * @param parent The parent. Not <code>null</code>.
+	 * @param comp The component, if applicable, can be <code>null</code>.
+	 * @return An array of lenth three (min,pref,max).
+	 */
+	final int[] getPixelSizes(float refSize, ContainerWrapper parent, ComponentWrapper comp)
+	{
+		return new int[] {
+				min != null ? min.getPixels(refSize, parent, comp) : 0,
+				pref != null ? pref.getPixels(refSize, parent, comp) : 0,
+				max != null ? max.getPixels(refSize, parent, comp) : LayoutUtil.INF
+		};
+	}
+
+	/** Returns the a constraint string that can be re-parsed to be the exact same UnitValue.
+	 * @return A String. Never <code>null</code>.
+	 */
+	String getConstraintString()
+	{
+		String cs = LayoutUtil.getCCString(this);
+		if (cs != null)
+			return cs;
+
+		if (min == pref && pref == max)
+			return min != null ? (min.getConstraintString() + "!") : "null";
+
+		StringBuilder sb = new StringBuilder(16);
+
+		if (min != null)
+			sb.append(min.getConstraintString()).append(':');
+
+		if (pref != null) {
+			if (min == null && max != null)
+				sb.append(":");
+			sb.append(pref.getConstraintString());
+		} else if (min != null) {
+			sb.append('n');
+		}
+
+		if (max != null)
+			sb.append(sb.length() == 0 ? "::" : ":").append(max.getConstraintString());
+
+		if (gapPush) {
+			if (sb.length() > 0)
+				sb.append(':');
+			sb.append("push");
+		}
+
+		return sb.toString();
+	}
+
+	void checkNotLinked()
+	{
+		if (min != null && min.isLinkedDeep() || pref != null && pref.isLinkedDeep() || max != null && max.isLinkedDeep())
+			throw new IllegalArgumentException("Size may not contain links");
+	}
+
+	static {
+        if(LayoutUtil.HAS_BEANS){
+            LayoutUtil.setDelegate(BoundSize.class, new PersistenceDelegate() {
+                protected Expression instantiate(Object oldInstance, Encoder out)
+                {
+                    BoundSize bs = (BoundSize) oldInstance;
+                    if (Grid.TEST_GAPS) {
+                        return new Expression(oldInstance, BoundSize.class, "new", new Object[] {
+                                bs.getMin(), bs.getPreferred(), bs.getMax(), bs.getGapPush(), bs.getConstraintString()
+                        });
+                    } else {
+                        return new Expression(oldInstance, BoundSize.class, "new", new Object[] {
+                                bs.getMin(), bs.getPreferred(), bs.getMax(), bs.getConstraintString()
+                        });
+                    }
+                }
+            });
+        }
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private static final long serialVersionUID = 1L;
+
+	protected Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	private void writeObject(ObjectOutputStream out) throws IOException
+	{
+		if (getClass() == BoundSize.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+
+	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+}
diff --git a/src/net/miginfocom/layout/CC.java b/src/net/miginfocom/layout/CC.java
new file mode 100644
index 0000000..6195847
--- /dev/null
+++ b/src/net/miginfocom/layout/CC.java
@@ -0,0 +1,1833 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+import java.util.ArrayList;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A simple value holder for one component's constraint.
+ */
+public final class CC implements Externalizable
+{
+	private static final BoundSize DEF_GAP = BoundSize.NULL_SIZE;    // Only used to denote default wrap/newline gap.
+
+	static final String[] DOCK_SIDES = {"north", "west", "south", "east"};
+
+	// See the getters and setters for information about the properties below.
+
+	private int dock = -1;
+
+	private UnitValue[] pos = null; // [x1, y1, x2, y2]
+
+	private UnitValue[] padding = null;   // top, left, bottom, right
+
+	private Boolean flowX = null;
+
+	private int skip = 0;
+
+	private int split = 1;
+
+	private int spanX = 1, spanY = 1;
+
+	private int cellX = -1, cellY = 0; // If cellX is -1 then cellY is also considered -1. cellY is never negative.
+
+	private String tag = null;
+
+	private String id = null;
+
+	private int hideMode = -1;
+
+	private DimConstraint hor = new DimConstraint();
+
+	private DimConstraint ver = new DimConstraint();
+
+	private BoundSize newline = null;
+
+	private BoundSize wrap = null;
+
+	private boolean boundsInGrid = true;
+
+	private boolean external = false;
+
+	private Float pushX = null, pushY = null;
+
+
+	// ***** Tmp cache field
+
+	private static final String[] EMPTY_ARR = new String[0];
+
+	private transient String[] linkTargets = null;
+
+	/** Empty constructor.
+	 */
+	public CC()
+	{
+	}
+
+	String[] getLinkTargets()
+	{
+		if (linkTargets == null) {
+			final ArrayList<String> targets = new ArrayList<String>(2);
+
+			if (pos != null) {
+				for (int i = 0; i < pos.length ; i++)
+					addLinkTargetIDs(targets, pos[i]);
+			}
+
+			linkTargets = targets.size() == 0 ? EMPTY_ARR : targets.toArray(new String[targets.size()]);
+		}
+		return linkTargets;
+	}
+
+	private void addLinkTargetIDs(ArrayList<String> targets, UnitValue uv)
+	{
+		if (uv != null) {
+			String linkId = uv.getLinkTargetId();
+			if (linkId != null) {
+				targets.add(linkId);
+			} else {
+				for (int i = uv.getSubUnitCount() - 1; i >= 0; i--) {
+					UnitValue subUv = uv.getSubUnitValue(i);
+					if (subUv.isLinkedDeep())
+						addLinkTargetIDs(targets, subUv);
+				}
+			}
+		}
+	}
+
+	// **********************************************************
+	// Chaining constraint setters
+	// **********************************************************
+
+	/** Specifies that the component should be put in the end group <code>s</code> and will thus share the same ending
+	 * coordinate as them within the group.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC endGroupX(String s)
+	{
+		hor.setEndGroup(s);
+		return this;
+	}
+
+	/** Specifies that the component should be put in the size group <code>s</code> and will thus share the same size
+	 * as them within the group.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s A name to associate on the group that should be the same for other rows/columns in the same group.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC sizeGroupX(String s)
+	{
+		hor.setSizeGroup(s);
+		return this;
+	}
+
+	/** The minimum size for the component. The value will override any value that is set on the component itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC minWidth(String size)
+	{
+		hor.setSize(LayoutUtil.derive(hor.getSize(), ConstraintParser.parseUnitValue(size, true), null, null));
+		return this;
+	}
+
+	/** The size for the component as a min and/or preferred and/or maximum size. The value will override any value that is set on
+	 * the component itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The size expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC width(String size)
+	{
+		hor.setSize(ConstraintParser.parseBoundSize(size, false, true));
+		return this;
+	}
+
+	/** The maximum size for the component. The value will override any value that is set on the component itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC maxWidth(String size)
+	{
+		hor.setSize(LayoutUtil.derive(hor.getSize(), null, null, ConstraintParser.parseUnitValue(size, true)));
+		return this;
+	}
+
+
+	/** The horizontal gap before and/or after the component. The gap is towards cell bounds and/or other component bounds.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param before The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @param after The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC gapX(String before, String after)
+	{
+		if (before != null)
+			hor.setGapBefore(ConstraintParser.parseBoundSize(before, true, true));
+
+		if (after != null)
+			hor.setGapAfter(ConstraintParser.parseBoundSize(after, true, true));
+
+		return this;
+	}
+
+	/** Same functionality as <code>getHorizontal().setAlign(ConstraintParser.parseUnitValue(unitValue, true))</code> only this method
+	 * returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param align The align keyword or for instance "100px". E.g "left", "right", "leading" or "trailing".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC alignX(String align)
+	{
+		hor.setAlign(ConstraintParser.parseUnitValueOrAlign(align, true, null));
+		return this;
+	}
+
+	/** The grow priority compared to other components in the same cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The grow priority.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC growPrioX(int p)
+	{
+		hor.setGrowPriority(p);
+		return this;
+	}
+
+	/** Grow priority for the component horizontally and optionally vertically.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC growPrio(int ... widthHeight)
+	{
+		switch (widthHeight.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+			case 2:
+				growPrioY(widthHeight[1]);
+			case 1:
+				growPrioX(widthHeight[0]);
+		}
+		return this;
+	}
+
+	/** Grow weight for the component horizontally. It default to weight <code>100</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #growX(float)
+	 */
+	public final CC growX()
+	{
+		hor.setGrow(ResizeConstraint.WEIGHT_100);
+		return this;
+	}
+
+	/** Grow weight for the component horizontally.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param w The new grow weight.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC growX(float w)
+	{
+		hor.setGrow(new Float(w));
+		return this;
+	}
+
+	/** grow weight for the component horizontally and optionally vertically.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC grow(float ... widthHeight)
+	{
+		switch (widthHeight.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+			case 2:
+				growY(widthHeight[1]);
+			case 1:
+				growX(widthHeight[0]);
+		}
+		return this;
+	}
+
+	/** The shrink priority compared to other components in the same cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The shrink priority.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC shrinkPrioX(int p)
+	{
+		hor.setShrinkPriority(p);
+		return this;
+	}
+
+	/** Shrink priority for the component horizontally and optionally vertically.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC shrinkPrio(int ... widthHeight)
+	{
+		switch (widthHeight.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+			case 2:
+				shrinkPrioY(widthHeight[1]);
+			case 1:
+				shrinkPrioX(widthHeight[0]);
+		}
+		return this;
+	}
+
+	/** Shrink weight for the component horizontally.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param w The new shrink weight.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC shrinkX(float w)
+	{
+		hor.setShrink(new Float(w));
+		return this;
+	}
+
+	/** Shrink weight for the component horizontally and optionally vertically.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param widthHeight The new shrink weight and height. 1-2 arguments, never null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC shrink(float ... widthHeight)
+	{
+		switch (widthHeight.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + widthHeight.length);
+			case 2:
+				shrinkY(widthHeight[1]);
+			case 1:
+				shrinkX(widthHeight[0]);
+		}
+		return this;
+	}
+
+	/** The end group that this component should be placed in.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s The name of the group. If <code>null</code> that means no group (default)
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC endGroupY(String s)
+	{
+		ver.setEndGroup(s);
+		return this;
+	}
+
+	/** The end group(s) that this component should be placed in.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param xy The end group for x and y respectively. 1-2 arguments, not null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC endGroup(String ... xy)
+	{
+		switch (xy.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + xy.length);
+			case 2:
+				endGroupY(xy[1]);
+			case 1:
+				endGroupX(xy[0]);
+		}
+		return this;
+	}
+
+	/** The size group that this component should be placed in.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s The name of the group. If <code>null</code> that means no group (default)
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC sizeGroupY(String s)
+	{
+		ver.setSizeGroup(s);
+		return this;
+	}
+
+	/** The size group(s) that this component should be placed in.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param xy The size group for x and y respectively. 1-2 arguments, not null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC sizeGroup(String ... xy)
+	{
+		switch (xy.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + xy.length);
+			case 2:
+				sizeGroupY(xy[1]);
+			case 1:
+				sizeGroupX(xy[0]);
+		}
+		return this;
+	}
+
+	/** The minimum size for the component. The value will override any value that is set on the component itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC minHeight(String size)
+	{
+		ver.setSize(LayoutUtil.derive(ver.getSize(), ConstraintParser.parseUnitValue(size, false), null, null));
+		return this;
+	}
+
+	/** The size for the component as a min and/or preferred and/or maximum size. The value will override any value that is set on
+	 * the component itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The size expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC height(String size)
+	{
+		ver.setSize(ConstraintParser.parseBoundSize(size, false, false));
+		return this;
+	}
+
+	/** The maximum size for the component. The value will override any value that is set on the component itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The size expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC maxHeight(String size)
+	{
+		ver.setSize(LayoutUtil.derive(ver.getSize(), null, null, ConstraintParser.parseUnitValue(size, false)));
+		return this;
+	}
+
+	/** The vertical gap before (normally above) and/or after (normally below) the component. The gap is towards cell bounds and/or other component bounds.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param before The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @param after The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC gapY(String before, String after)
+	{
+		if (before != null)
+			ver.setGapBefore(ConstraintParser.parseBoundSize(before, true, false));
+
+		if (after != null)
+			ver.setGapAfter(ConstraintParser.parseBoundSize(after, true, false));
+
+		return this;
+	}
+
+	/** Same functionality as <code>getVertical().setAlign(ConstraintParser.parseUnitValue(unitValue, true))</code> only this method
+	 * returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param align The align keyword or for instance "100px". E.g "top" or "bottom".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC alignY(String align)
+	{
+		ver.setAlign(ConstraintParser.parseUnitValueOrAlign(align, false, null));
+		return this;
+	}
+
+	/** The grow priority compared to other components in the same cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The grow priority.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC growPrioY(int p)
+	{
+		ver.setGrowPriority(p);
+		return this;
+	}
+
+	/** Grow weight for the component vertically. Defaults to <code>100</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #growY(Float)
+	 */
+	public final CC growY()
+	{
+		ver.setGrow(ResizeConstraint.WEIGHT_100);
+		return this;
+	}
+
+	/** Grow weight for the component vertically.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param w The new grow weight.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC growY(Float w)
+	{
+		ver.setGrow(w);
+		return this;
+	}
+
+	/** The shrink priority compared to other components in the same cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The shrink priority.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC shrinkPrioY(int p)
+	{
+		ver.setShrinkPriority(p);
+		return this;
+	}
+
+	/** Shrink weight for the component horizontally.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param w The new shrink weight.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC shrinkY(float w)
+	{
+		ver.setShrink(new Float(w));
+		return this;
+	}
+
+	/** How this component, if hidden (not visible), should be treated.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param mode The mode. Default to the mode in the {@link net.miginfocom.layout.LC}.
+	 * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+	 * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+	 * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+	 * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC hideMode(int mode)
+	{
+		setHideMode(mode);
+		return this;
+	}
+
+	/** The id used to reference this component in some constraints.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s The id or <code>null</code>. May consist of a groupID and an componentID which are separated by a dot: ".". E.g. "grp1.id1".
+	 * The dot should never be first or last if present.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final CC id(String s)
+	{
+		setId(s);
+		return this;
+	}
+
+	/** Same functionality as {@link #setTag(String tag)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param tag The new tag. May be <code>null</code>.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setTag(String)
+	 */
+	public final CC tag(String tag)
+	{
+		setTag(tag);
+		return this;
+	}
+
+	/** Set the cell(s) that the component should occupy in the grid. Same functionality as {@link #setCellX(int col)} and
+	 * {@link #setCellY(int row)} together with {@link #setSpanX(int width)} and {@link #setSpanY(int height)}. This method
+	 * returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param colRowWidthHeight cellX, cellY, spanX, spanY repectively. 1-4 arguments, not null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setCellX(int)
+	 * @see #setCellY(int)
+	 * @see #setSpanX(int)
+	 * @see #setSpanY(int)
+	 * @since 3.7.2. Replacing cell(int, int) and cell(int, int, int, int)
+	 */
+	public final CC cell(int ... colRowWidthHeight)
+	{
+		switch (colRowWidthHeight.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + colRowWidthHeight.length);
+			case 4:
+				setSpanY(colRowWidthHeight[3]);
+			case 3:
+				setSpanX(colRowWidthHeight[2]);
+			case 2:
+				setCellY(colRowWidthHeight[1]);
+			case 1:
+				setCellX(colRowWidthHeight[0]);
+		}
+		return this;
+	}
+
+	/** Same functionality as <code>spanX(cellsX).spanY(cellsY)</code> which means this cell will span cells in both x and y.
+	 * This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * Since 3.7.2 this takes an array/vararg whereas it previously only took two specific values, xSpan and ySpan.
+	 * @param cells spanX and spanY, when present, and in that order.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSpanY(int)
+	 * @see #setSpanX(int)
+	 * @see #spanY()
+	 * @see #spanX()
+	 * @since 3.7.2 Replaces span(int, int).
+	 */
+	public final CC span(int ... cells)
+	{
+		if (cells == null || cells.length == 0) {
+			setSpanX(LayoutUtil.INF);
+			setSpanY(1);
+		} else if (cells.length == 1) {
+			setSpanX(cells[0]);
+			setSpanY(1);
+		} else {
+			setSpanX(cells[0]);
+			setSpanY(cells[1]);
+		}
+		return this;
+	}
+
+	/** Corresponds exactly to the "gap left right top bottom" keyword.
+	 * @param args Same as for the "gap" keyword. Length 1-4, never null buf elements can be null.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC gap(String ... args)
+	{
+		switch (args.length) {
+			default:
+				throw new IllegalArgumentException("Illegal argument count: " + args.length);
+			case 4:
+				gapBottom(args[3]);
+			case 3:
+				gapTop(args[2]);
+			case 2:
+				gapRight(args[1]);
+			case 1:
+				gapLeft(args[0]);
+		}
+		return this;
+	}
+
+	/** Sets the horizontal gap before the component.
+	 * <p>
+	 * Note! This is currently same as gapLeft(). This might change in 4.x.
+	 * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC gapBefore(String boundsSize)
+	{
+		hor.setGapBefore(ConstraintParser.parseBoundSize(boundsSize, true, true));
+		return this;
+	}
+
+	/** Sets the horizontal gap after the component.
+	 * <p>
+	 * Note! This is currently same as gapLeft(). This might change in 4.x.
+	 * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC gapAfter(String boundsSize)
+	{
+		hor.setGapAfter(ConstraintParser.parseBoundSize(boundsSize, true, true));
+		return this;
+	}
+
+	/** Sets the gap above the component.
+	 * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC gapTop(String boundsSize)
+	{
+		ver.setGapBefore(ConstraintParser.parseBoundSize(boundsSize, true, false));
+		return this;
+	}
+
+	/** Sets the gap to the left the component.
+	 * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC gapLeft(String boundsSize)
+	{
+		hor.setGapBefore(ConstraintParser.parseBoundSize(boundsSize, true, true));
+		return this;
+	}
+
+	/** Sets the gap below the component.
+	 * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC gapBottom(String boundsSize)
+	{
+		ver.setGapAfter(ConstraintParser.parseBoundSize(boundsSize, true, false));
+		return this;
+	}
+
+	/** Sets the gap to the right of the component.
+	 * @param boundsSize The size of the gap expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px!".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final CC gapRight(String boundsSize)
+	{
+		hor.setGapAfter(ConstraintParser.parseBoundSize(boundsSize, true, true));
+		return this;
+	}
+
+	/** Same functionality as {@link #setSpanY(int LayoutUtil.INF)} which means this cell will span the rest of the column.
+	 * This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSpanY(int)
+	 * @see #spanY()
+	 */
+	public final CC spanY()
+	{
+		return spanY(LayoutUtil.INF);
+	}
+
+	/** Same functionality as {@link #setSpanY(int)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param cells The number of cells to span (i.e. merge).
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSpanY(int)
+	 */
+	public final CC spanY(int cells)
+	{
+		setSpanY(cells);
+		return this;
+	}
+
+	/** Same functionality as {@link #setSpanX(int)} which means this cell will span the rest of the row.
+	 * This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSpanX(int)
+	 * @see #spanX()
+	 */
+	public final CC spanX()
+	{
+		return spanX(LayoutUtil.INF);
+	}
+
+	/** Same functionality as {@link #setSpanX(int)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param cells The number of cells to span (i.e. merge).
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSpanY(int)
+	 */
+	public final CC spanX(int cells)
+	{
+		setSpanX(cells);
+		return this;
+	}
+
+	/** Same functionality as <code>pushX().pushY()</code> which means this cell will push in both x and y dimensions.
+	 * This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPushX(Float)
+	 * @see #setPushX(Float)
+	 * @see #pushY()
+	 * @see #pushX()
+	 */
+	public final CC push()
+	{
+		return pushX().pushY();
+	}
+
+	/** Same functionality as <code>pushX(weightX).pushY(weightY)</code> which means this cell will push in both x and y dimensions.
+	 * This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param weightX The weight used in the push.
+	 * @param weightY The weight used in the push.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPushY(Float)
+	 * @see #setPushX(Float)
+	 * @see #pushY()
+	 * @see #pushX()
+	 */
+	public final CC push(Float weightX, Float weightY)
+	{
+		return pushX(weightX).pushY(weightY);
+	}
+
+	/** Same functionality as {@link #setPushY(Float))} which means this cell will push the rest of the column.
+	 * This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPushY(Float)
+	 */
+	public final CC pushY()
+	{
+		return pushY(ResizeConstraint.WEIGHT_100);
+	}
+
+	/** Same functionality as {@link #setPushY(Float weight)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param weight The weight used in the push.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPushY(Float)
+	 */
+	public final CC pushY(Float weight)
+	{
+		setPushY(weight);
+		return this;
+	}
+
+	/** Same functionality as {@link #setPushX(Float)} which means this cell will push the rest of the row.
+	 * This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPushX(Float)
+	 */
+	public final CC pushX()
+	{
+		return pushX(ResizeConstraint.WEIGHT_100);
+	}
+
+	/** Same functionality as {@link #setPushX(Float weight)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param weight The weight used in the push.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPushY(Float)
+	 */
+	public final CC pushX(Float weight)
+	{
+		setPushX(weight);
+		return this;
+	}
+
+	/** Same functionality as {@link #setSplit(int parts)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param parts The number of parts (i.e. component slots) the cell should be divided into.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSplit(int)
+	 */
+	public final CC split(int parts)
+	{
+		setSplit(parts);
+		return this;
+	}
+
+	/** Same functionality as split(LayoutUtil.INF), which means split until one of the keywords that breaks the split is found for
+	 * a component after this one (e.g. wrap, newline and skip).
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSplit(int)
+	 * @since 3.7.2
+	 */
+	public final CC split()
+	{
+		setSplit(LayoutUtil.INF);
+		return this;
+	}
+
+	/** Same functionality as {@link #setSkip(int)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param cells How many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSkip(int)
+	 */
+	public final CC skip(int cells)
+	{
+		setSkip(cells);
+		return this;
+	}
+
+	/** Same functionality as skip(1).
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setSkip(int)
+	 * @since 3.7.2
+	 */
+	public final CC skip()
+	{
+		setSkip(1);
+		return this;
+	}
+
+	/** Same functionality as {@link #setExternal(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setExternal(boolean)
+	 */
+	public final CC external()
+	{
+		setExternal(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setFlowX(Boolean .TRUE)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setFlowX(Boolean)
+	 */
+	public final CC flowX()
+	{
+		setFlowX(Boolean.TRUE);
+		return this;
+	}
+
+	/** Same functionality as {@link #setFlowX(Boolean .FALSE)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setFlowX(Boolean)
+	 */
+	public final CC flowY()
+	{
+		setFlowX(Boolean.FALSE);
+		return this;
+	}
+
+
+	/** Same functionality as {@link #growX()} and {@link #growY()}.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #growX()
+	 * @see #growY()
+	 */
+	public final CC grow()
+	{
+		growX();
+		growY();
+		return this;
+	}
+
+	/** Same functionality as {@link #setNewline(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setNewline(boolean)
+	 */
+	public final CC newline()
+	{
+		setNewline(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setNewlineGapSize(BoundSize)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param gapSize The gap size that will override the gap size in the row/colum constraints if <code>!= null</code>. E.g. "5px" or "unrel".
+	 * If <code>null</code> or <code>""</code> the newline size will be set to the default size and turned on. This is different compared to
+	 * {@link #setNewlineGapSize(BoundSize)}.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setNewlineGapSize(BoundSize)
+	 */
+	public final CC newline(String gapSize)
+	{
+		BoundSize bs = ConstraintParser.parseBoundSize(gapSize, true, (flowX != null && flowX == false));
+		if (bs != null) {
+			setNewlineGapSize(bs);
+		} else {
+			setNewline(true);
+		}
+		return this;
+	}
+
+	/** Same functionality as {@link #setWrap(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setWrap(boolean)
+	 */
+	public final CC wrap()
+	{
+		setWrap(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setWrapGapSize(BoundSize)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param gapSize The gap size that will override the gap size in the row/colum constraints if <code>!= null</code>. E.g. "5px" or "unrel".
+	 * If <code>null</code> or <code>""</code> the wrap size will be set to the default size and turned on. This is different compared to
+	 * {@link #setWrapGapSize(BoundSize)}.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setWrapGapSize(BoundSize)
+	 */
+	public final CC wrap(String gapSize)
+	{
+		BoundSize bs = ConstraintParser.parseBoundSize(gapSize, true, (flowX != null && flowX == false));
+		if (bs != null) {
+			setWrapGapSize(bs);
+		} else {
+			setWrap(true);
+		}
+		return this;
+	}
+
+	/** Same functionality as {@link #setDockSide(int 0)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setDockSide(int)
+	 */
+	public final CC dockNorth()
+	{
+		setDockSide(0);
+		return this;
+	}
+
+	/** Same functionality as {@link #setDockSide(int 1)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setDockSide(int)
+	 */
+	public final CC dockWest()
+	{
+		setDockSide(1);
+		return this;
+	}
+
+	/** Same functionality as {@link #setDockSide(int 2)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setDockSide(int)
+	 */
+	public final CC dockSouth()
+	{
+		setDockSide(2);
+		return this;
+	}
+
+	/** Same functionality as {@link #setDockSide(int 3)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setDockSide(int)
+	 */
+	public final CC dockEast()
+	{
+		setDockSide(3);
+		return this;
+	}
+
+	/** Sets the x-coordinate for the component. This is used to set the x coordinate position to a specific value. The component
+	 * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the x position.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param x The x position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPos(UnitValue[])
+	 * @see #setBoundsInGrid(boolean)
+	 */
+	public final CC x(String x)
+	{
+		return corrPos(x, 0);
+	}
+
+	/** Sets the y-coordinate for the component. This is used to set the y coordinate position to a specific value. The component
+	 * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the y position.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param y The y position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPos(UnitValue[])
+	 * @see #setBoundsInGrid(boolean)
+	 */
+	public final CC y(String y)
+	{
+		return corrPos(y, 1);
+	}
+
+	/** Sets the x2-coordinate for the component (right side). This is used to set the x2 coordinate position to a specific value. The component
+	 * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the x position.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param x2 The x2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPos(UnitValue[])
+	 * @see #setBoundsInGrid(boolean)
+	 */
+	public final CC x2(String x2)
+	{
+		return corrPos(x2, 2);
+	}
+
+	/** Sets the y2-coordinate for the component (bottom side). This is used to set the y2 coordinate position to a specific value. The component
+	 * bounds is still precalculated to the grid cell and this method should be seen as a way to correct the y position.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param y2 The y2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPos(UnitValue[])
+	 * @see #setBoundsInGrid(boolean)
+	 */
+	public final CC y2(String y2)
+	{
+		return corrPos(y2, 3);
+	}
+
+	private final CC corrPos(String uv, int ix)
+	{
+		UnitValue[] b = getPos();
+		if (b == null)
+			b = new UnitValue[4];
+
+		b[ix] = ConstraintParser.parseUnitValue(uv, (ix % 2 == 0));
+		setPos(b);
+
+		setBoundsInGrid(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #x(String x)} and {@link #y(String y)} toghether.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param x The x position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+	 * @param y The y position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPos(UnitValue[])
+	 */
+	public final CC pos(String x, String y)
+	{
+		UnitValue[] b = getPos();
+		if (b == null)
+			b = new UnitValue[4];
+
+		b[0] = ConstraintParser.parseUnitValue(x, true);
+		b[1] = ConstraintParser.parseUnitValue(y, false);
+		setPos(b);
+
+		setBoundsInGrid(false);
+		return this;
+	}
+
+	/** Same functionality as {@link #x(String x)}, {@link #y(String y)}, {@link #y2(String y)} and {@link #y2(String y)} toghether.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param x The x position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+	 * @param y The y position as a UnitValue. E.g. "10" or "40mm" or "container.x+10".
+	 * @param x2 The x2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+	 * @param y2 The y2 side's position as a UnitValue. E.g. "10" or "40mm" or "container.x2 - 10".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setPos(UnitValue[])
+	 */
+	public final CC pos(String x, String y, String x2, String y2)
+	{
+		setPos(new UnitValue[] {
+				ConstraintParser.parseUnitValue(x, true),
+				ConstraintParser.parseUnitValue(y, false),
+				ConstraintParser.parseUnitValue(x2, true),
+				ConstraintParser.parseUnitValue(y2, false),
+		});
+		setBoundsInGrid(false);
+		return this;
+	}
+
+	/** Same functionality as {@link #setPadding(UnitValue[])} but the unit values as absolute pixels. This method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param top The top padding that will be added to the y coordinate at the last stage in the layout.
+	 * @param left The top padding that will be added to the x coordinate at the last stage in the layout.
+	 * @param bottom The top padding that will be added to the y2 coordinate at the last stage in the layout.
+	 * @param right The top padding that will be added to the x2 coordinate at the last stage in the layout.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setTag(String)
+	 */
+	public final CC pad(int top, int left, int bottom, int right)
+	{
+		setPadding(new UnitValue[] {
+				new UnitValue(top),	new UnitValue(left), new UnitValue(bottom), new UnitValue(right)
+		});
+		return this;
+	}
+
+	/** Same functionality as <code>setPadding(ConstraintParser.parseInsets(pad, false))}</code> only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param pad The string to parse. E.g. "10 10 10 10" or "20". If less than 4 groups the last will be used for the missing.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new ComponentConstraint().noGrid().gap().fill()</code>.
+	 * @see #setTag(String)
+	 */
+	public final CC pad(String pad)
+	{
+		setPadding(pad != null ? ConstraintParser.parseInsets(pad, false) : null);
+		return this;
+	}
+
+	// **********************************************************
+	// Bean properties
+	// **********************************************************
+
+	/** Returns the horizontal dimension constraint for this component constraint. It has constraints for the horizontal size
+	 * and grow/shink priorities and weights.
+	 * <p>
+	 * Note! If any changes is to be made it must be made direct when the object is returned. It is not allowed to save the
+	 * constraint for later use.
+	 * @return The current dimension constraint. Never <code>null</code>.
+	 */
+	public DimConstraint getHorizontal()
+	{
+		return hor;
+	}
+
+	/** Sets the horizontal dimension constraint for this component constraint. It has constraints for the horizontal size
+	 * and grow/shrink priorities and weights.
+	 * @param h The new dimension constraint. If <code>null</code> it will be reset to <code>new DimConstraint();</code>
+	 */
+	public void setHorizontal(DimConstraint h)
+	{
+		hor = h != null ? h : new DimConstraint();
+	}
+
+	/** Returns the vertical dimension constraint for this component constraint. It has constraints for the vertical size
+	 * and grow/shrink priorities and weights.
+	 * <p>
+	 * Note! If any changes is to be made it must be made direct when the object is returned. It is not allowed to save the
+	 * constraint for later use.
+	 * @return The current dimension constraint. Never <code>null</code>.
+	 */
+	public DimConstraint getVertical()
+	{
+		return ver;
+	}
+
+	/** Sets the vertical dimension constraint for this component constraint. It has constraints for the vertical size
+	 * and grow/shrink priorities and weights.
+	 * @param v The new dimension constraint. If <code>null</code> it will be reset to <code>new DimConstraint();</code>
+	 */
+	public void setVertical(DimConstraint v)
+	{
+		ver = v != null ? v : new DimConstraint();
+	}
+
+	/** Returns the vertical or horizontal dim constraint.
+	 * <p>
+	 * Note! If any changes is to be made it must be made direct when the object is returned. It is not allowed to save the
+	 * constraint for later use.
+	 * @param isHor If the horizontal constraint should be returned.
+	 * @return The dim constraint. Never <code>null</code>.
+	 */
+	public DimConstraint getDimConstraint(boolean isHor)
+	{
+		return isHor ? hor : ver;
+	}
+
+	/** Returns the absolute positioning of one or more of the edges. This will be applied last in the layout cycle and will not
+	 * affect the flow or grid positions. The positioning is relative to the parent and can not (as padding) be used
+	 * to adjust the edges relative to the old value. May be <code>null</code> and elements may be <code>null</code>.
+	 * <code>null</code> value(s) for the x2 and y2 will be interpreted as to keep the preferred size and thus the x1
+	 * and x2 will just absolutely positions the component.
+	 * <p>
+	 * Note that {@link #setBoundsInGrid(boolean)} changes the interpretation of thisproperty slightly.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value as a new array, free to modify.
+	 */
+	public UnitValue[] getPos()
+	{
+		return pos != null ? new UnitValue[] {pos[0], pos[1], pos[2], pos[3]} : null;
+	}
+
+	/** Sets absolute positioning of one or more of the edges. This will be applied last in the layout cycle and will not
+	 * affect the flow or grid positions. The positioning is relative to the parent and can not (as padding) be used
+	 * to adjust the edges relative to the old value. May be <code>null</code> and elements may be <code>null</code>.
+	 * <code>null</code> value(s) for the x2 and y2 will be interpreted as to keep the preferred size and thus the x1
+	 * and x2 will just absolutely positions the component.
+	 * <p>
+	 * Note that {@link #setBoundsInGrid(boolean)} changes the interpretation of thisproperty slightly.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param pos <code>UnitValue[] {x, y, x2, y2}</code>. Must be <code>null</code> or of length 4. Elements can be <code>null</code>.
+	 */
+	public void setPos(UnitValue[] pos)
+	{
+		this.pos = pos != null ? new UnitValue[] {pos[0], pos[1], pos[2], pos[3]} : null;
+		linkTargets = null;
+	}
+
+	/** Returns if the absolute <code>pos</code> value should be corrections to the component that is in a normal cell. If <code>false</code>
+	 * the value of <code>pos</code> is truly absolute in that it will not affect the grid or have a default bounds in the grid.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 * @see #getPos()
+	 */
+	public boolean isBoundsInGrid()
+	{
+		return boundsInGrid;
+	}
+
+	/** Sets if the absolute <code>pos</code> value should be corrections to the component that is in a normal cell. If <code>false</code>
+	 * the value of <code>pos</code> is truly absolute in that it will not affect the grid or have a default bounds in the grid.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>true</code> for bounds taken from the grid position. <code>false</code> is default.
+	 * @see #setPos(UnitValue[])
+	 */
+	void setBoundsInGrid(boolean b)
+	{
+		this.boundsInGrid = b;
+	}
+
+	/** Returns the absolute cell position in the grid or <code>-1</code> if cell positioning is not used.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public int getCellX()
+	{
+		return cellX;
+	}
+
+	/** Set an absolute cell x-position in the grid. If >= 0 this point points to the absolute cell that this constaint's component should occupy.
+	 * If there's already a component in that cell they will split the cell. The flow will then continue after this cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param x The x-position or <code>-1</code> to disable cell positioning.
+	 */
+	public void setCellX(int x)
+	{
+		cellX = x;
+	}
+
+	/** Returns the absolute cell position in the grid or <code>-1</code> if cell positioning is not used.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public int getCellY()
+	{
+		return cellX < 0 ? -1 : cellY;
+	}
+
+	/** Set an absolute cell x-position in the grid. If >= 0 this point points to the absolute cell that this constaint's component should occupy.
+	 * If there's already a component in that cell they will split the cell. The flow will then continue after this cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param y The y-position or <code>-1</code> to disable cell positioning.
+	 */
+	public void setCellY(int y)
+	{
+		if (y < 0)
+			cellX = -1;
+		cellY = y < 0 ? 0 : y;
+	}
+
+	/** Sets the docking side. -1 means no docking.<br>
+	 * Valid sides are: <code> north = 0, west = 1, south = 2, east = 3</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current side.
+	 */
+	public int getDockSide()
+	{
+		return dock;
+	}
+
+	/** Sets the docking side. -1 means no docking.<br>
+	 * Valid sides are: <code> north = 0, west = 1, south = 2, east = 3</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param side -1 or 0-3.
+	 */
+	public void setDockSide(int side)
+	{
+		if (side < -1 || side > 3)
+			throw new IllegalArgumentException("Illegal dock side: " + side);
+		dock = side;
+	}
+
+	/** Returns if this component should have its bounds handled by an external source and not this layout manager.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public boolean isExternal()
+	{
+		return external;
+	}
+
+	/** If this boolean is true this component is not handled in any way by the layout manager and the component can have its bounds set by an external
+	 * handler which is normally by the use of some <code>component.setBounds(x, y, width, height)</code> directly (for Swing).
+	 * <p>
+	 * The bounds <b>will not</b> affect the minimum and preferred size of the container.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>true</code> means that the bounds are not changed.
+	 */
+	public void setExternal(boolean b)
+	{
+		this.external = b;
+	}
+
+	/** Returns if the flow in the <b>cell</b> is in the horizontal dimension. Vertical if <code>false</code>. Only the first
+	 * component is a cell can set the flow.
+	 * <p>
+	 * If <code>null</code> the flow direction is inherited by from the {@link net.miginfocom.layout.LC}.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public Boolean getFlowX()
+	{
+		return flowX;
+	}
+
+	/** Sets if the flow in the <b>cell</b> is in the horizontal dimension. Vertical if <code>false</code>. Only the first
+	 * component is a cell can set the flow.
+	 * <p>
+	 * If <code>null</code> the flow direction is inherited by from the {@link net.miginfocom.layout.LC}.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>Boolean.TRUE</code> means horizontal flow in the cell.
+	 */
+	public void setFlowX(Boolean b)
+	{
+		this.flowX = b;
+	}
+
+	/** Sets how a component that is hidden (not visible) should be treated by default.
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The mode:<br>
+	 * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+	 * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+	 * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+	 * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+	 */
+	public int getHideMode()
+	{
+		return hideMode;
+	}
+
+	/** Sets how a component that is hidden (not visible) should be treated by default.
+	 * @param mode The mode:<br>
+	 * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+	 * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+	 * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+	 * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+	 */
+	public void setHideMode(int mode)
+	{
+		if (mode < -1 || mode > 3)
+			throw new IllegalArgumentException("Wrong hideMode: " + mode);
+
+		hideMode = mode;
+	}
+
+	/** Returns the id used to reference this component in some constraints.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The id or <code>null</code>. May consist of a groupID and an componentID which are separated by a dot: ".". E.g. "grp1.id1".
+	 * The dot should never be first or last if present.
+	 */
+	public String getId()
+	{
+		return id;
+	}
+
+	/** Sets the id used to reference this component in some constraints.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param id The id or <code>null</code>. May consist of a groupID and an componentID which are separated by a dot: ".". E.g. "grp1.id1".
+	 * The dot should never be first or last if present.
+	 */
+	public void setId(String id)
+	{
+		this.id = id;
+	}
+
+	/** Returns the absolute resizing in the last stage of the layout cycle. May be <code>null</code> and elements may be <code>null</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value. <code>null</code> or of length 4.
+	 */
+	public UnitValue[] getPadding()
+	{
+		return padding != null ? new UnitValue[] {padding[0], padding[1], padding[2], padding[3]} : null;
+	}
+
+	/** Sets the absolute resizing in the last stage of the layout cycle. These values are added to the edges and can thus for
+	 * instance be used to grow or reduce the size or move the component an absolute number of pixels. May be <code>null</code>
+	 * and elements may be <code>null</code>.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param sides top, left, bottom right. Must be <code>null</code> or of length 4.
+	 */
+	public void setPadding(UnitValue[] sides)
+	{
+		this.padding = sides != null ? new UnitValue[] {sides[0], sides[1], sides[2], sides[3]} : null;
+	}
+
+	/** Returns how many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to.
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value. 0 if no skip.
+	 */
+	public int getSkip()
+	{
+		return skip;
+	}
+
+	/** Sets how many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to.
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param cells How many cells in the grid that should be skipped <b>before</b> the component that this constraint belongs to
+	 */
+	public void setSkip(int cells)
+	{
+		this.skip = cells;
+	}
+
+	/** Returns the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+	 * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public int getSpanX()
+	{
+		return spanX;
+	}
+
+	/** Sets the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+	 * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param cells The number of cells to span (i.e. merge).
+	 */
+	public void setSpanX(int cells)
+	{
+		this.spanX = cells;
+	}
+
+	/** Returns the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+	 * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public int getSpanY()
+	{
+		return spanY;
+	}
+
+	/** Sets the number of cells the cell that this constraint's component will span in the indicated dimension. <code>1</code> is default and
+	 * means that it only spans the current cell. <code>LayoutUtil.INF</code> is used to indicate a span to the end of the column/row.
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param cells The number of cells to span (i.e. merge).
+	 */
+	public void setSpanY(int cells)
+	{
+		this.spanY = cells;
+	}
+
+	/** "pushx" indicates that the column that this component is in (this first if the component spans) should default to growing.
+	 * If any other column has been set to grow this push value on the component does nothing as the column's explicit grow weight
+	 * will take precedence. Push is normally used when the grid has not been defined in the layout.
+	 * <p>
+	 * If multiple components in a column has push weights set the largest one will be used for the column.
+	 * @return The current push value. Default is <code>null</code>.
+	 */
+	public Float getPushX()
+	{
+		return pushX;
+	}
+
+	/** "pushx" indicates that the column that this component is in (this first if the component spans) should default to growing.
+	 * If any other column has been set to grow this push value on the component does nothing as the column's explicit grow weight
+	 * will take precedence. Push is normally used when the grid has not been defined in the layout.
+	 * <p>
+	 * If multiple components in a column has push weights set the largest one will be used for the column.
+	 * @param weight The new push value. Default is <code>null</code>.
+	 */
+	public void setPushX(Float weight)
+	{
+		this.pushX = weight;
+	}
+
+	/** "pushx" indicates that the row that this component is in (this first if the component spans) should default to growing.
+	 * If any other row has been set to grow this push value on the component does nothing as the row's explicit grow weight
+	 * will take precedence. Push is normally used when the grid has not been defined in the layout.
+	 * <p>
+	 * If multiple components in a row has push weights set the largest one will be used for the row.
+	 * @return The current push value. Default is <code>null</code>.
+	 */
+	public Float getPushY()
+	{
+		return pushY;
+	}
+
+	/** "pushx" indicates that the row that this component is in (this first if the component spans) should default to growing.
+	 * If any other row has been set to grow this push value on the component does nothing as the row's explicit grow weight
+	 * will take precedence. Push is normally used when the grid has not been defined in the layout.
+	 * <p>
+	 * If multiple components in a row has push weights set the largest one will be used for the row.
+	 * @param weight The new push value. Default is <code>null</code>.
+	 */
+	public void setPushY(Float weight)
+	{
+		this.pushY = weight;
+	}
+
+	/** Returns in how many parts the current cell (that this constraint's component will be in) should be split in. If for instance
+	 * it is split in two, the next component will also share the same cell. Note that the cell can also span a number of
+	 * cells, which means that you can for instance span three cells and split that big cell for two components. Split can be
+	 * set to a very high value to make all components in the same row/column share the same cell (e.g. <code>LayoutUtil.INF</code>).
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public int getSplit()
+	{
+		return split;
+	}
+
+	/** Sets in how many parts the current cell (that this constraint's component will be in) should be split in. If for instance
+	 * it is split in two, the next component will also share the same cell. Note that the cell can also span a number of
+	 * cells, which means that you can for instance span three cells and split that big cell for two components. Split can be
+	 * set to a very high value to make all components in the same row/column share the same cell (e.g. <code>LayoutUtil.INF</code>).
+	 * <p>
+	 * Note that only the first component will be checked for this property.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param parts The number of parts (i.e. component slots) the cell should be divided into.
+	 */
+	public void setSplit(int parts)
+	{
+		this.split = parts;
+	}
+
+	/** Tags the component with metadata. Currently only used to tag buttons with for instance "cancel" or "ok" to make them
+	 * show up in the correct order depending on platform. See {@link PlatformDefaults#setButtonOrder(String)} for information.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value. May be <code>null</code>.
+	 */
+	public String getTag()
+	{
+		return tag;
+	}
+
+	/** Optinal tag that gives more context to this constraint's component. It is for instance used to tag buttons in a
+	 * button bar with the button type such as "ok", "help" or "cancel".
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param tag The new tag. May be <code>null</code>.
+	 */
+	public void setTag(String tag)
+	{
+		this.tag = tag;
+	}
+
+	/** Returns if the flow should wrap to the next line/column <b>after</b> the component that this constraint belongs to.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public boolean isWrap()
+	{
+		return wrap != null;
+	}
+
+	/** Sets if the flow should wrap to the next line/column <b>after</b> the component that this constraint belongs to.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>true</code> means wrap after.
+	 */
+	public void setWrap(boolean b)
+	{
+		wrap = b ? (wrap == null ? DEF_GAP : wrap) : null;
+	}
+
+	/** Returns the wrap size if it is a custom size. If wrap was set to true with {@link #setWrap(boolean)} then this method will
+	 * return <code>null</code> since that means that the gap size should be the default one as defined in the rows spec.
+	 * @return The custom gap size. NOTE! Will return <code>null</code> for both no wrap <b>and</b> default wrap.
+	 * @see #isWrap()
+	 * @see #setWrap(boolean)
+	 * @since 2.4.2
+	 */
+	public BoundSize getWrapGapSize()
+	{
+		return wrap == DEF_GAP ? null : wrap;
+	}
+
+	/** Set the wrap size and turns wrap on if <code>!= null</code>.
+	 * @param s The custom gap size. NOTE! <code>null</code> will not turn on or off wrap, it will only set the wrap gap size to "default".
+	 * A non-null value will turn on wrap though.
+	 * @see #isWrap()
+	 * @see #setWrap(boolean)
+	 * @since 2.4.2
+	 */
+	public void setWrapGapSize(BoundSize s)
+	{
+		wrap = s == null ? (wrap != null ? DEF_GAP : null) : s;
+	}
+
+	/** Returns if the flow should wrap to the next line/column <b>before</b> the component that this constraint belongs to.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current value.
+	 */
+	public boolean isNewline()
+	{
+		return newline != null;
+	}
+
+	/** Sets if the flow should wrap to the next line/column <b>before</b> the component that this constraint belongs to.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>true</code> means wrap before.
+	 */
+	public void setNewline(boolean b)
+	{
+		newline = b ? (newline == null ? DEF_GAP : newline) : null;
+	}
+
+	/** Returns the newline size if it is a custom size. If newline was set to true with {@link #setNewline(boolean)} then this method will
+	 * return <code>null</code> since that means that the gap size should be the default one as defined in the rows spec.
+	 * @return The custom gap size. NOTE! Will return <code>null</code> for both no newline <b>and</b> default newline.
+	 * @see #isNewline()
+	 * @see #setNewline(boolean)
+	 * @since 2.4.2
+	 */
+	public BoundSize getNewlineGapSize()
+	{
+		return newline == DEF_GAP ? null : newline;
+	}
+
+	/** Set the newline size and turns newline on if <code>!= null</code>.
+	 * @param s The custom gap size. NOTE! <code>null</code> will not turn on or off newline, it will only set the newline gap size to "default".
+	 * A non-null value will turn on newline though.
+	 * @see #isNewline()
+	 * @see #setNewline(boolean)
+	 * @since 2.4.2
+	 */
+	public void setNewlineGapSize(BoundSize s)
+	{
+		newline = s == null ? (newline != null ? DEF_GAP : null) : s;
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+
+	public void writeExternal(ObjectOutput out) throws IOException
+	{
+		if (getClass() == CC.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+}
\ No newline at end of file
diff --git a/src/net/miginfocom/layout/ComponentWrapper.java b/src/net/miginfocom/layout/ComponentWrapper.java
new file mode 100644
index 0000000..60fc8f8
--- /dev/null
+++ b/src/net/miginfocom/layout/ComponentWrapper.java
@@ -0,0 +1,297 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A class that wraps the important parts of a Component.
+ * <p>
+ * <b>NOTE!</b>.equals() and .hashcode() should be shunted to the wrapped component. E.g.
+ * <pre>
+ * 	public int hashCode()
+	{
+		return getComponent().hashCode();
+	}
+
+	public final boolean equals(Object o)
+	{
+		 if (o instanceof ComponentWrapper == false)
+			 return false;
+
+		 return getComponent().equals(((ComponentWrapper) o).getComponent());
+	}
+ * </pre>
+ */
+public interface ComponentWrapper
+{
+	static final int TYPE_UNSET = -1;
+	public static final int TYPE_UNKNOWN = 0;
+	public static final int TYPE_CONTAINER = 1;
+	public static final int TYPE_LABEL = 2;
+	public static final int TYPE_TEXT_FIELD = 3;
+	public static final int TYPE_TEXT_AREA = 4;
+	public static final int TYPE_BUTTON = 5;
+	public static final int TYPE_LIST = 6;
+	public static final int TYPE_TABLE = 7;
+	public static final int TYPE_SCROLL_PANE = 8;
+	public static final int TYPE_IMAGE = 9;
+	public static final int TYPE_PANEL = 10;
+	public static final int TYPE_COMBO_BOX = 11;
+	public static final int TYPE_SLIDER = 12;
+	public static final int TYPE_SPINNER = 13;
+	public static final int TYPE_PROGRESS_BAR = 14;
+	public static final int TYPE_TREE = 15;
+	public static final int TYPE_CHECK_BOX = 16;
+	public static final int TYPE_SCROLL_BAR = 17;
+	public static final int TYPE_SEPARATOR = 18;
+
+	/** Returns the actual object that this wrapper is aggregating. This might be needed for getting
+	 * information about the object that the wrapper interface does not provide.
+	 * <p>
+	 * If this is a container the container should be returned instead.
+	 * @return The actual object that this wrapper is aggregating. Not <code>null</code>.
+	 */
+	public abstract Object getComponent();
+
+	/** Returns the current x coordinate for this component.
+	 * @return The current x coordinate for this component.
+	 */
+	public abstract int getX();
+
+	/** Returns the current y coordinate for this component.
+	 * @return The current y coordinate for this component.
+	 */
+	public abstract int getY();
+
+	/** Returns the current width for this component.
+	 * @return The current width for this component.
+	 */
+	public abstract int getWidth();
+
+	/** Returns the current height for this component.
+	 * @return The current height for this component.
+	 */
+	public abstract int getHeight();
+
+	/** Returns the screen x-coordinate for the upper left coordinate of the component layout-able bounds.
+	 * @return The screen x-coordinate for the upper left coordinate of the component layout-able bounds.
+	 */
+	public abstract int getScreenLocationX();
+
+	/** Returns the screen y-coordinate for the upper left coordinate of the component layout-able bounds.
+	 * @return The screen y-coordinate for the upper left coordinate of the component layout-able bounds.
+	 */
+	public abstract int getScreenLocationY();
+
+	/** Returns the minimum width of the component.
+	 * @param hHint The Size hint for the other dimension. An implementation can use this value or the
+	 * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+	 * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+	 * @return The minimum width of the component.
+	 * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+	 * any implementing classes. This change was worth it though.
+	 */
+	public abstract int getMinimumWidth(int hHint);
+
+	/** Returns the minimum height of the component.
+	 * @param wHint The Size hint for the other dimension. An implementation can use this value or the
+	 * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+	 * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+	 * @return The minimum height of the component.
+	 * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+	 * any implementing classes. This change was worth it though.
+	 */
+	public abstract int getMinimumHeight(int wHint);
+
+	/** Returns the preferred width of the component.
+	 * @param hHint The Size hint for the other dimension. An implementation can use this value or the
+	 * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+	 * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+	 * @return The preferred width of the component.
+	 * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+	 * any implementing classes. This change was worth it though.
+	 */
+	public abstract int getPreferredWidth(int hHint);
+
+	/** Returns the preferred height of the component.
+	 * @param wHint The Size hint for the other dimension. An implementation can use this value or the
+	 * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+	 * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+	 * @return The preferred height of the component.
+	 * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+	 * any implementing classes. This change was worth it though.
+	 */
+	public abstract int getPreferredHeight(int wHint);
+
+	/** Returns the maximum width of the component.
+	 * @param hHint The Size hint for the other dimension. An implementation can use this value or the
+	 * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+	 * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+	 * @return The maximum width of the component.
+	 * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+	 * any implementing classes. This change was worth it though.
+	 */
+	public abstract int getMaximumWidth(int hHint);
+
+	/** Returns the maximum height of the component.
+	 * @param wHint The Size hint for the other dimension. An implementation can use this value or the
+	 * current size for the widget in this dimension, or a combination of both, to calculate the correct size.<br>
+	 * Use -1 to denote that there is no hint. This corresponds with SWT.DEFAULT.
+	 * @return The maximum height of the component.
+	 * @since 3.5. Added the hint as a parameter knowing that a correction and recompilation is necessary for
+	 * any implementing classes. This change was worth it though.
+	 */
+	public abstract int getMaximumHeight(int wHint);
+
+	/** Sets the component's bounds.
+	 * @param x The x coordinate.
+	 * @param y The y coordinate.
+	 * @param width The width.
+	 * @param height The height.
+	 */
+	public abstract void setBounds(int x, int y, int width, int height);
+
+	/** Returns if the component's visibility is set to <code>true</code>. This should not return if the component is
+	 * actually visible, but if the visibility is set to true or not.
+	 * @return <code>true</code> means visible.
+	 */
+	public abstract boolean isVisible();
+
+	/** Returns the baseline for the component given the suggested height.
+	 * @param width The width to calculate for if other than the current. If <code>-1</code> the current size should be used.
+	 * @param height The height to calculate for if other than the current. If <code>-1</code> the current size should be used.
+	 * @return The baseline from the top or -1 if not applicable.
+	 */
+	public abstract int getBaseline(int width, int height);
+
+	/** Returns if the component has a baseline and if it can be retrieved. Should for instance return
+	 * <code>false</code> for Swing before mustang.
+	 * @return If the component has a baseline and if it can be retrieved.
+	 */
+	public abstract boolean hasBaseline();
+
+	/** Returns the container for this component.
+	 * @return The container for this component. Will return <code>null</code> if the component has no parent.
+	 */
+	public abstract ContainerWrapper getParent();
+
+	/** Returns the pixel unit factor for the horizontal or vertical dimension.
+	 * <p>
+	 * The factor is 1 for both dimensions on the normal font in a JPanel on Windows. The factor should increase with a bigger "X".
+	 * <p>
+	 * This is the Swing version:
+	 * <pre>
+	 * Rectangle2D r = fm.getStringBounds("X", parent.getGraphics());
+	 * wFactor = r.getWidth() / 6;
+	 * hFactor = r.getHeight() / 13.27734375f;
+	 * </pre>
+	 * @param isHor If it is the horizontal factor that should be returned.
+	 * @return The factor.
+	 */
+	public abstract float getPixelUnitFactor(boolean isHor);
+
+	/** Returns the DPI (Dots Per Inch) of the screen the component is currently in or for the default
+	 * screen if the component is not visible.
+	 * <p>
+	 * If headless mode {@link net.miginfocom.layout.PlatformDefaults#getDefaultDPI} will be returned.
+	 * @return The DPI.
+	 */
+	public abstract int getHorizontalScreenDPI();
+
+	/** Returns the DPI (Dots Per Inch) of the screen the component is currently in or for the default
+	 * screen if the component is not visible.
+	 * <p>
+	 * If headless mode {@link net.miginfocom.layout.PlatformDefaults#getDefaultDPI} will be returned.
+	 * @return The DPI.
+	 */
+	public abstract int getVerticalScreenDPI();
+
+	/** Returns the pixel size of the screen that the component is currently in or for the default
+	 * screen if the component is not visible or <code>null</code>.
+	 * <p>
+	 * If in headless mode <code>1024</code> is returned.
+	 * @return The screen size. E.g. <code>1280</code>.
+	 */
+	public abstract int getScreenWidth();
+
+	/** Returns the pixel size of the screen that the component is currently in or for the default
+	 * screen if the component is not visible or <code>null</code>.
+	 * <p>
+	 * If in headless mode <code>768</code> is returned.
+	 * @return The screen size. E.g. <code>1024</code>.
+	 */
+	public abstract int getScreenHeight();
+
+	/** Returns a String id that can be used to reference the component in link constraints. This value should
+	 * return the default id for the component. The id can be set for a component in the constraints and if
+	 * so the value returned by this method will never be used. If there are no sensible id for the component
+	 * <code>null</code> should be returned.
+	 * <p>
+	 * For instance the Swing implementation returns the string returned from <code>Component.getName()</code>.
+	 * @return The string link id or <code>null</code>.
+	 */
+	public abstract String getLinkId();
+
+	/** Returns a hash code that should be reasonably different for anything that might change the layout. This value is used to
+	 *  know if the component layout needs to clear any caches.
+	 * @return A hash code that should be reasonably different for anything that might change the layout. Returns -1 if the widget is
+	 * disposed.
+	 */
+	public abstract int getLayoutHashCode();
+
+	/** Returns the padding on a component by component basis. This method can be overridden to return padding to compensate for example for
+	 * borders that have shadows or where the outer most pixel is not the visual "edge" to align to.
+	 * <p>
+	 * Default implementation returns <code>null</code> for all components except for Windows XP's JTabbedPane which will return new Insets(0, 0, 2, 2).
+	 * <p>
+	 * <b>NOTE!</B> To reduce generated garbage the returned padding should never be changed so that the same insets can be returned many times.
+	 * @return <code>null</code> if no padding. <b>NOTE!</B> To reduce generated garbage the returned padding should never be changed so that
+	 * the same insets can be returned many times. [top, left, bottom, right]
+	 */
+	public int[] getVisualPadding();
+
+	/** Paints component outline to indicate where it is.
+	 */
+	public abstract void paintDebugOutline();
+
+	/** Returns the type of component that this wrapper is wrapping.
+	 * <p>
+	 * This method can be invoked often so the result should be cached.
+	 * <p>
+	 * <b>NOTE!</b> This is misspelled. Keeping it that way though since this is only used by developers who
+	 * port MigLayout.
+	 * @param disregardScrollPane Is <code>true</code> any wrapping scroll pane should be disregarded and the type
+	 * of the scrolled component should be returned.
+	 * @return The type of component that this wrapper is wrapping. E.g. {@link #TYPE_LABEL}.
+	 */
+	public abstract int getComponetType(boolean disregardScrollPane);
+}
\ No newline at end of file
diff --git a/src/net/miginfocom/layout/ConstraintParser.java b/src/net/miginfocom/layout/ConstraintParser.java
new file mode 100644
index 0000000..ccf171c
--- /dev/null
+++ b/src/net/miginfocom/layout/ConstraintParser.java
@@ -0,0 +1,1464 @@
+package net.miginfocom.layout;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** Parses string constraints.
+ */
+public final class ConstraintParser
+{
+	private ConstraintParser()
+	{
+	}
+
+	/** Parses the layout constraints and stores the parsed values in the transient (cache) member varables.
+	 * @param s The String to parse. Should not be <code>null</code> and <b>must be lower case and trimmed</b>.
+	 * @throws RuntimeException if the constaint was not valid.
+	 * @return The parsed constraint. Never <code>null</code>.
+	 */
+	public static LC parseLayoutConstraint(String s)
+	{
+		LC lc = new LC();
+		if (s.length() == 0)
+			return lc;
+
+		String[] parts = toTrimmedTokens(s, ',');
+
+		// First check for "ltr" or "rtl" since that will affect the interpretation of the other constraints.
+		for (int i = 0; i < parts.length; i++) {
+			String part = parts[i];
+			if (part == null)
+				continue;
+
+			int len = part.length();
+			if (len == 3 || len == 11) {   // Optimization
+				if (part.equals("ltr") || part.equals("rtl") || part.equals("lefttoright") || part.equals("righttoleft")) {
+					lc.setLeftToRight(part.charAt(0) == 'l' ? Boolean.TRUE : Boolean.FALSE);
+					parts[i] = null;    // So we will not try to interpret it again
+				}
+
+				if (part.equals("ttb") || part.equals("btt") || part.equals("toptobottom") || part.equals("bottomtotop")) {
+					lc.setTopToBottom(part.charAt(0) == 't');
+					parts[i] = null;    // So we will not try to interpret it again
+				}
+			}
+		}
+
+		for (String part : parts) {
+			if (part == null || part.length() == 0)
+				continue;
+
+			try {
+				int ix = -1;
+				char c = part.charAt(0);
+
+				if (c == 'w' || c == 'h') {
+
+					ix = startsWithLenient(part, "wrap", -1, true);
+					if (ix > -1) {
+						String num = part.substring(ix).trim();
+						lc.setWrapAfter(num.length() != 0 ? Integer.parseInt(num) : 0);
+						continue;
+					}
+
+					boolean isHor = c == 'w';
+					if (isHor && (part.startsWith("w ") || part.startsWith("width "))) {
+						String sz = part.substring(part.charAt(1) == ' ' ? 2 : 6).trim();
+						lc.setWidth(parseBoundSize(sz, false, true));
+						continue;
+					}
+
+					if (!isHor && (part.startsWith("h ") || part.startsWith("height "))) {
+						String uvStr = part.substring(part.charAt(1) == ' ' ? 2 : 7).trim();
+						lc.setHeight(parseBoundSize(uvStr, false, false));
+						continue;
+					}
+
+					if (part.length() > 5) {
+						String sz = part.substring(5).trim();
+						if (part.startsWith("wmin ")) {
+							lc.minWidth(sz);
+							continue;
+						} else if (part.startsWith("wmax ")) {
+							lc.maxWidth(sz);
+							continue;
+						} else if (part.startsWith("hmin ")) {
+							lc.minHeight(sz);
+							continue;
+						} else if (part.startsWith("hmax ")) {
+							lc.maxHeight(sz);
+							continue;
+						}
+					}
+
+					if (part.startsWith("hidemode ")) {
+						lc.setHideMode(Integer.parseInt(part.substring(9)));
+						continue;
+					}
+				}
+
+				if (c == 'g') {
+					if (part.startsWith("gapx ")) {
+						lc.setGridGapX(parseBoundSize(part.substring(5).trim(), true, true));
+						continue;
+					}
+
+					if (part.startsWith("gapy ")) {
+						lc.setGridGapY(parseBoundSize(part.substring(5).trim(), true, false));
+						continue;
+					}
+
+					if (part.startsWith("gap ")) {
+						String[] gaps = toTrimmedTokens(part.substring(4).trim(), ' ');
+						lc.setGridGapX(parseBoundSize(gaps[0], true, true));
+						lc.setGridGapY(gaps.length > 1 ? parseBoundSize(gaps[1], true, false) : lc.getGridGapX());
+						continue;
+					}
+				}
+
+				if (c == 'd') {
+					ix = startsWithLenient(part, "debug", 5, true);
+					if (ix > -1) {
+						String millis = part.substring(ix).trim();
+						lc.setDebugMillis(millis.length() > 0 ? Integer.parseInt(millis) : 1000);
+						continue;
+					}
+				}
+
+				if (c == 'n') {
+					if (part.equals("nogrid")) {
+						lc.setNoGrid(true);
+						continue;
+					}
+
+					if (part.equals("nocache")) {
+						lc.setNoCache(true);
+						continue;
+					}
+
+					if (part.equals("novisualpadding")) {
+						lc.setVisualPadding(false);
+						continue;
+					}
+				}
+
+				if (c == 'f') {
+					if (part.equals("fill") || part.equals("fillx") || part.equals("filly")) {
+						lc.setFillX(part.length() == 4 || part.charAt(4) == 'x');
+						lc.setFillY(part.length() == 4 || part.charAt(4) == 'y');
+						continue;
+					}
+
+					if (part.equals("flowy")) {
+						lc.setFlowX(false);
+						continue;
+					}
+
+					if (part.equals("flowx")) {
+						lc.setFlowX(true); // This is the default but added for consistency
+						continue;
+					}
+				}
+
+				if (c == 'i') {
+					ix = startsWithLenient(part, "insets", 3, true);
+					if (ix > -1) {
+						String insStr = part.substring(ix).trim();
+						UnitValue[] ins = parseInsets(insStr, true);
+						LayoutUtil.putCCString(ins, insStr);
+						lc.setInsets(ins);
+						continue;
+					}
+				}
+
+				if (c == 'a') {
+					ix = startsWithLenient(part, new String[]{"aligny", "ay"}, new int[]{6, 2}, true);
+					if (ix > -1) {
+						UnitValue align = parseUnitValueOrAlign(part.substring(ix).trim(), false, null);
+						if (align == UnitValue.BASELINE_IDENTITY)
+							throw new IllegalArgumentException("'baseline' can not be used to align the whole component group.");
+						lc.setAlignY(align);
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"alignx", "ax"}, new int[]{6, 2}, true);
+					if (ix > -1) {
+						lc.setAlignX(parseUnitValueOrAlign(part.substring(ix).trim(), true, null));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "align", 2, true);
+					if (ix > -1) {
+						String[] gaps = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						lc.setAlignX(parseUnitValueOrAlign(gaps[0], true, null));
+						if (gaps.length > 1)
+							lc.setAlignY(parseUnitValueOrAlign(gaps[1], false, null));
+						continue;
+					}
+				}
+
+				if (c == 'p') {
+					if (part.startsWith("packalign ")) {
+						String[] packs = toTrimmedTokens(part.substring(10).trim(), ' ');
+						lc.setPackWidthAlign(packs[0].length() > 0 ? Float.parseFloat(packs[0]) : 0.5f);
+						if (packs.length > 1)
+							lc.setPackHeightAlign(Float.parseFloat(packs[1]));
+						continue;
+					}
+
+					if (part.startsWith("pack ") || part.equals("pack")) {
+						String ps = part.substring(4).trim();
+						String[] packs = toTrimmedTokens(ps.length() > 0 ? ps : "pref pref", ' ');
+						lc.setPackWidth(parseBoundSize(packs[0], false, true));
+						if (packs.length > 1)
+							lc.setPackHeight(parseBoundSize(packs[1], false, false));
+
+						continue;
+					}
+				}
+
+				if (lc.getAlignX() == null) {
+					UnitValue alignX = parseAlignKeywords(part, true);
+					if (alignX != null) {
+						lc.setAlignX(alignX);
+						continue;
+					}
+				}
+
+				UnitValue alignY = parseAlignKeywords(part, false);
+				if (alignY != null) {
+					lc.setAlignY(alignY);
+					continue;
+				}
+
+				throw new IllegalArgumentException("Unknown Constraint: '" + part + "'\n");
+
+			} catch (Exception ex) {
+				throw new IllegalArgumentException("Illegal Constraint: '" + part + "'\n" + ex.getMessage());
+			}
+		}
+
+//		lc = (LC) serializeTest(lc);
+
+		return lc;
+	}
+
+	/** Parses the column or rows constraints. They normally looks something like <code>"[min:pref]rel[10px][]"</code>.
+	 * @param s The string to parse. Not <code>null</code>.
+	 * @return An array of {@link DimConstraint}s that is as many are there exist "[...]" sections in the string that is parsed.
+	 * @throws RuntimeException if the constraint was not valid.
+	 */
+	public static AC parseRowConstraints(String s)
+	{
+		return parseAxisConstraint(s, false);
+	}
+
+	/** Parses the column or rows constraints. They normally looks something like <code>"[min:pref]rel[10px][]"</code>.
+	 * @param s The string to parse. Not <code>null</code>.
+	 * @return An array of {@link DimConstraint}s that is as many are there exist "[...]" sections in the string that is parsed.
+	 * @throws RuntimeException if the constraint was not valid.
+	 */
+	public static AC parseColumnConstraints(String s)
+	{
+		return parseAxisConstraint(s, true);
+	}
+
+	/** Parses the column or rows constraints. They normally looks something like <code>"[min:pref]rel[10px][]"</code>.
+	 * @param s The string to parse. Not <code>null</code>.
+	 * @param isCols If this for columns rather than rows.
+	 * @return An array of {@link DimConstraint}s that is as many are there exist "[...]" sections in the string that is parsed.
+	 * @throws RuntimeException if the constraint was not valid.
+	 */
+	private static AC parseAxisConstraint(String s, boolean isCols)
+	{
+		s = s.trim();
+
+		if (s.length() == 0)
+			return new AC();    // Short circuit for performance.
+
+		s = s.toLowerCase();
+
+		ArrayList<String> parts = getRowColAndGapsTrimmed(s);
+
+		BoundSize[] gaps = new BoundSize[(parts.size() >> 1) + 1];
+		for (int i = 0, iSz = parts.size(), gIx = 0; i < iSz; i += 2, gIx++)
+			gaps[gIx] = parseBoundSize(parts.get(i), true, isCols);
+
+		DimConstraint[] colSpecs = new DimConstraint[parts.size() >> 1];
+		for (int i = 0, gIx = 0; i < colSpecs.length; i++, gIx++) {
+			if (gIx >= gaps.length - 1)
+				gIx = gaps.length - 2;
+
+			colSpecs[i] = parseDimConstraint(parts.get((i << 1) + 1), gaps[gIx], gaps[gIx + 1], isCols);
+		}
+
+		AC ac = new AC();
+		ac.setConstaints(colSpecs);
+
+//		ac = (AC) serializeTest(ac);
+
+		return ac;
+	}
+
+	/** Parses a single column or row constraint.
+	 * @param s The single constraint to parse. May look something like <code>"min:pref,fill,grow"</code>. Should not be <code>null</code> and <b>must
+	 * be lower case and trimmed</b>.
+	 * @param gapBefore The default gap "before" the column/row constraint. Can be overridden with a <code>"gap"</code> section within <code>s</code>.
+	 * @param gapAfter The default gap "after" the column/row constraint. Can be overridden with a <code>"gap"</code> section within <code>s</code>.
+	 * @param isCols If the constraints are column constraints rather than row constraints.
+	 * @return A single constraint. Never <code>null</code>.
+	 * @throws RuntimeException if the constraint was not valid.
+	 */
+	private static DimConstraint parseDimConstraint(String s, BoundSize gapBefore, BoundSize gapAfter, boolean isCols)
+	{
+		DimConstraint dimConstraint = new DimConstraint();
+
+		// Default values.
+		dimConstraint.setGapBefore(gapBefore);
+		dimConstraint.setGapAfter(gapAfter);
+
+		String[] parts = toTrimmedTokens(s, ',');
+		for (int i = 0; i < parts.length; i++) {
+			String part = parts[i];
+			try {
+				if (part.length() == 0)
+					continue;
+
+				if (part.equals("fill")) {
+					dimConstraint.setFill(true);
+//					 dimConstraint.setAlign(null);   // Can not have both fill and alignment (changed for 3.5 since it can have "growy 0")
+					continue;
+				}
+
+				if (part.equals("nogrid")) {
+					dimConstraint.setNoGrid(true);
+					continue;
+				}
+
+				int ix = -1;
+				char c = part.charAt(0);
+
+				if (c == 's') {
+					ix = startsWithLenient(part, new String[] {"sizegroup", "sg"}, new int[] {5, 2}, true);
+					if (ix > -1) {
+						dimConstraint.setSizeGroup(part.substring(ix).trim());
+						continue;
+					}
+
+
+					ix = startsWithLenient(part, new String[] {"shrinkprio", "shp"}, new int[] {10, 3}, true);
+					if (ix > -1) {
+						dimConstraint.setShrinkPriority(Integer.parseInt(part.substring(ix).trim()));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "shrink", 6, true);
+					if (ix > -1) {
+						dimConstraint.setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+				}
+
+				if (c == 'g') {
+					ix = startsWithLenient(part, new String[] {"growpriority", "gp"}, new int[] {5, 2}, true);
+					if (ix > -1) {
+						dimConstraint.setGrowPriority(Integer.parseInt(part.substring(ix).trim()));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "grow", 4, true);
+					if (ix > -1) {
+						dimConstraint.setGrow(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+				}
+
+				if (c == 'a') {
+					ix = startsWithLenient(part, "align", 2, true);
+					if (ix > -1) {
+//						if (dimConstraint.isFill() == false)    // Swallow, but ignore if fill is set. (changed for 3.5 since it can have "growy 0")
+							dimConstraint.setAlign(parseUnitValueOrAlign(part.substring(ix).trim(), isCols, null));
+						continue;
+					}
+				}
+
+				UnitValue align = parseAlignKeywords(part, isCols);
+				if (align != null) {
+//					if (dimConstraint.isFill() == false)    // Swallow, but ignore if fill is set. (changed for 3.5 since it can have "growy 0")
+						dimConstraint.setAlign(align);
+					continue;
+				}
+
+				 // Only min:pref:max still left that is ok
+				dimConstraint.setSize(parseBoundSize(part, false, isCols));
+
+			} catch (Exception ex) {
+				throw new IllegalArgumentException("Illegal contraint: '" + part + "'\n" + ex.getMessage());
+			}
+		}
+		return dimConstraint;
+	}
+
+	/** Parses all component constraints and stores the parsed values in the transient (cache) member varables.
+	 * @param constrMap The constraints as <code>String</code>s. Strings <b>must be lower case and trimmed</b>
+	 * @return The parsed constraints. Never <code>null</code>.
+	 */
+	public static Map<ComponentWrapper, CC> parseComponentConstraints(Map<ComponentWrapper, String> constrMap)
+	{
+		HashMap<ComponentWrapper, CC> flowConstrMap = new HashMap<ComponentWrapper, CC>();
+
+		for (Iterator<Map.Entry<ComponentWrapper, String>> it = constrMap.entrySet().iterator(); it.hasNext();) {
+			Map.Entry<ComponentWrapper, String> entry = it.next();
+			flowConstrMap.put(entry.getKey(), parseComponentConstraint(entry.getValue()));
+		}
+
+		return flowConstrMap;
+	}
+
+	/** Parses one component constraint and returns the parsed value.
+	 * @param s The string to parse. Should not be <code>null</code> and <b>must be lower case and trimmed</b>.
+	 * @throws RuntimeException if the constaint was not valid.
+	 * @return The parsed constraint. Never <code>null</code>.
+	 */
+	public static CC parseComponentConstraint(String s)
+	{
+		CC cc = new CC();
+
+		if (s.length() == 0)
+			return cc;
+
+		String[] parts = toTrimmedTokens(s, ',');
+
+		for (String part : parts) {
+			try {
+				if (part.length() == 0)
+					continue;
+
+				int ix = -1;
+				char c = part.charAt(0);
+
+				if (c == 'n') {
+					if (part.equals("north")) {
+						cc.setDockSide(0);
+						continue;
+					}
+
+					if (part.equals("newline")) {
+						cc.setNewline(true);
+						continue;
+					}
+
+					if (part.startsWith("newline ")) {
+						String gapSz = part.substring(7).trim();
+						cc.setNewlineGapSize(parseBoundSize(gapSz, true, true));
+						continue;
+					}
+				}
+
+				if (c == 'f' && (part.equals("flowy") || part.equals("flowx"))) {
+					cc.setFlowX(part.charAt(4) == 'x' ? Boolean.TRUE : Boolean.FALSE);
+					continue;
+				}
+
+				if (c == 's') {
+					ix = startsWithLenient(part, "skip", 4, true);
+					if (ix > -1) {
+						String num = part.substring(ix).trim();
+						cc.setSkip(num.length() != 0 ? Integer.parseInt(num) : 1);
+						continue;
+					}
+
+					ix = startsWithLenient(part, "split", 5, true);
+					if (ix > -1) {
+						String split = part.substring(ix).trim();
+						cc.setSplit(split.length() > 0 ? Integer.parseInt(split) : LayoutUtil.INF);
+						continue;
+					}
+
+					if (part.equals("south")) {
+						cc.setDockSide(2);
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"spany", "sy"}, new int[]{5, 2}, true);
+					if (ix > -1) {
+						cc.setSpanY(parseSpan(part.substring(ix).trim()));
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"spanx", "sx"}, new int[]{5, 2}, true);
+					if (ix > -1) {
+						cc.setSpanX(parseSpan(part.substring(ix).trim()));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "span", 4, true);
+					if (ix > -1) {
+						String[] spans = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						cc.setSpanX(spans[0].length() > 0 ? Integer.parseInt(spans[0]) : LayoutUtil.INF);
+						cc.setSpanY(spans.length > 1 ? Integer.parseInt(spans[1]) : 1);
+						continue;
+					}
+
+					ix = startsWithLenient(part, "shrinkx", 7, true);
+					if (ix > -1) {
+						cc.getHorizontal().setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "shrinky", 7, true);
+					if (ix > -1) {
+						cc.getVertical().setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "shrink", 6, false);
+					if (ix > -1) {
+						String[] shrinks = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						cc.getHorizontal().setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						if (shrinks.length > 1)
+							cc.getVertical().setShrink(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"shrinkprio", "shp"}, new int[]{10, 3}, true);
+					if (ix > -1) {
+						String sp = part.substring(ix).trim();
+						if (sp.startsWith("x") || sp.startsWith("y")) { // To gandle "gpx", "gpy", "shrinkpriorityx", shrinkpriorityy"
+							(sp.startsWith("x") ? cc.getHorizontal() : cc.getVertical()).setShrinkPriority(Integer.parseInt(sp.substring(2)));
+						} else {
+							String[] shrinks = toTrimmedTokens(sp, ' ');
+							cc.getHorizontal().setShrinkPriority(Integer.parseInt(shrinks[0]));
+							if (shrinks.length > 1)
+								cc.getVertical().setShrinkPriority(Integer.parseInt(shrinks[1]));
+						}
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"sizegroupx", "sizegroupy", "sgx", "sgy"}, new int[]{9, 9, 2, 2}, true);
+					if (ix > -1) {
+						String sg = part.substring(ix).trim();
+						char lc = part.charAt(ix - 1);
+						if (lc != 'y')
+							cc.getHorizontal().setSizeGroup(sg);
+						if (lc != 'x')
+							cc.getVertical().setSizeGroup(sg);
+						continue;
+					}
+				}
+
+				if (c == 'g') {
+					ix = startsWithLenient(part, "growx", 5, true);
+					if (ix > -1) {
+						cc.getHorizontal().setGrow(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "growy", 5, true);
+					if (ix > -1) {
+						cc.getVertical().setGrow(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "grow", 4, false);
+					if (ix > -1) {
+						String[] grows = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						cc.getHorizontal().setGrow(parseFloat(grows[0], ResizeConstraint.WEIGHT_100));
+						cc.getVertical().setGrow(parseFloat(grows.length > 1 ? grows[1] : "", ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"growprio", "gp"}, new int[]{8, 2}, true);
+					if (ix > -1) {
+						String gp = part.substring(ix).trim();
+						char c0 = gp.length() > 0 ? gp.charAt(0) : ' ';
+						if (c0 == 'x' || c0 == 'y') { // To gandle "gpx", "gpy", "growpriorityx", growpriorityy"
+							(c0 == 'x' ? cc.getHorizontal() : cc.getVertical()).setGrowPriority(Integer.parseInt(gp.substring(2)));
+						} else {
+							String[] grows = toTrimmedTokens(gp, ' ');
+							cc.getHorizontal().setGrowPriority(Integer.parseInt(grows[0]));
+							if (grows.length > 1)
+								cc.getVertical().setGrowPriority(Integer.parseInt(grows[1]));
+						}
+						continue;
+					}
+
+					if (part.startsWith("gap")) {
+						BoundSize[] gaps = parseGaps(part); // Changes order!!
+						if (gaps[0] != null)
+							cc.getVertical().setGapBefore(gaps[0]);
+						if (gaps[1] != null)
+							cc.getHorizontal().setGapBefore(gaps[1]);
+						if (gaps[2] != null)
+							cc.getVertical().setGapAfter(gaps[2]);
+						if (gaps[3] != null)
+							cc.getHorizontal().setGapAfter(gaps[3]);
+						continue;
+					}
+				}
+
+				if (c == 'a') {
+					ix = startsWithLenient(part, new String[]{"aligny", "ay"}, new int[]{6, 2}, true);
+					if (ix > -1) {
+						cc.getVertical().setAlign(parseUnitValueOrAlign(part.substring(ix).trim(), false, null));
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"alignx", "ax"}, new int[]{6, 2}, true);
+					if (ix > -1) {
+						cc.getHorizontal().setAlign(parseUnitValueOrAlign(part.substring(ix).trim(), true, null));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "align", 2, true);
+					if (ix > -1) {
+						String[] gaps = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						cc.getHorizontal().setAlign(parseUnitValueOrAlign(gaps[0], true, null));
+						if (gaps.length > 1)
+							cc.getVertical().setAlign(parseUnitValueOrAlign(gaps[1], false, null));
+						continue;
+					}
+				}
+
+				if ((c == 'x' || c == 'y') && part.length() > 2) {
+					char c2 = part.charAt(1);
+					if (c2 == ' ' || (c2 == '2' && part.charAt(2) == ' ')) {
+						if (cc.getPos() == null) {
+							cc.setPos(new UnitValue[4]);
+						} else if (cc.isBoundsInGrid() == false) {
+							throw new IllegalArgumentException("Cannot combine 'position' with 'x/y/x2/y2' keywords.");
+						}
+
+						int edge = (c == 'x' ? 0 : 1) + (c2 == '2' ? 2 : 0);
+						UnitValue[] pos = cc.getPos();
+						pos[edge] = parseUnitValue(part.substring(2).trim(), null, c == 'x');
+						cc.setPos(pos);
+						cc.setBoundsInGrid(true);
+						continue;
+					}
+				}
+
+				if (c == 'c') {
+					ix = startsWithLenient(part, "cell", 4, true);
+					if (ix > -1) {
+						String[] grs = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						if (grs.length < 2)
+							throw new IllegalArgumentException("At least two integers must follow " + part);
+						cc.setCellX(Integer.parseInt(grs[0]));
+						cc.setCellY(Integer.parseInt(grs[1]));
+						if (grs.length > 2)
+							cc.setSpanX(Integer.parseInt(grs[2]));
+						if (grs.length > 3)
+							cc.setSpanY(Integer.parseInt(grs[3]));
+						continue;
+					}
+				}
+
+				if (c == 'p') {
+					ix = startsWithLenient(part, "pos", 3, true);
+					if (ix > -1) {
+						if (cc.getPos() != null && cc.isBoundsInGrid())
+							throw new IllegalArgumentException("Can not combine 'pos' with 'x/y/x2/y2' keywords.");
+
+						String[] pos = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						UnitValue[] bounds = new UnitValue[4];
+						for (int j = 0; j < pos.length; j++)
+							bounds[j] = parseUnitValue(pos[j], null, j % 2 == 0);
+
+						if (bounds[0] == null && bounds[2] == null || bounds[1] == null && bounds[3] == null)
+							throw new IllegalArgumentException("Both x and x2 or y and y2 can not be null!");
+
+						cc.setPos(bounds);
+						cc.setBoundsInGrid(false);
+						continue;
+					}
+
+					ix = startsWithLenient(part, "pad", 3, true);
+					if (ix > -1) {
+						UnitValue[] p = parseInsets(part.substring(ix).trim(), false);
+						cc.setPadding(new UnitValue[]{
+							p[0],
+							p.length > 1 ? p[1] : null,
+							p.length > 2 ? p[2] : null,
+							p.length > 3 ? p[3] : null});
+						continue;
+					}
+
+					ix = startsWithLenient(part, "pushx", 5, true);
+					if (ix > -1) {
+						cc.setPushX(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "pushy", 5, true);
+					if (ix > -1) {
+						cc.setPushY(parseFloat(part.substring(ix).trim(), ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+
+					ix = startsWithLenient(part, "push", 4, false);
+					if (ix > -1) {
+						String[] pushs = toTrimmedTokens(part.substring(ix).trim(), ' ');
+						cc.setPushX(parseFloat(pushs[0], ResizeConstraint.WEIGHT_100));
+						cc.setPushY(parseFloat(pushs.length > 1 ? pushs[1] : "", ResizeConstraint.WEIGHT_100));
+						continue;
+					}
+				}
+
+				if (c == 't') {
+					ix = startsWithLenient(part, "tag", 3, true);
+					if (ix > -1) {
+						cc.setTag(part.substring(ix).trim());
+						continue;
+					}
+				}
+
+				if (c == 'w' || c == 'h') {
+					if (part.equals("wrap")) {
+						cc.setWrap(true);
+						continue;
+					}
+
+					if (part.startsWith("wrap ")) {
+						String gapSz = part.substring(5).trim();
+						cc.setWrapGapSize(parseBoundSize(gapSz, true, true));
+						continue;
+					}
+
+					boolean isHor = c == 'w';
+					if (isHor && (part.startsWith("w ") || part.startsWith("width "))) {
+						String uvStr = part.substring(part.charAt(1) == ' ' ? 2 : 6).trim();
+						cc.getHorizontal().setSize(parseBoundSize(uvStr, false, true));
+						continue;
+					}
+
+					if (!isHor && (part.startsWith("h ") || part.startsWith("height "))) {
+						String uvStr = part.substring(part.charAt(1) == ' ' ? 2 : 7).trim();
+						cc.getVertical().setSize(parseBoundSize(uvStr, false, false));
+						continue;
+					}
+
+					if (part.startsWith("wmin ") || part.startsWith("wmax ") || part.startsWith("hmin ") || part.startsWith("hmax ")) {
+						String uvStr = part.substring(5).trim();
+						if (uvStr.length() > 0) {
+							UnitValue uv = parseUnitValue(uvStr, null, isHor);
+							boolean isMin = part.charAt(3) == 'n';
+							DimConstraint dc = isHor ? cc.getHorizontal() : cc.getVertical();
+							dc.setSize(new BoundSize(
+								isMin ? uv : dc.getSize().getMin(),
+								dc.getSize().getPreferred(),
+								isMin ? (dc.getSize().getMax()) : uv,
+								uvStr
+							));
+							continue;
+						}
+					}
+
+					if (part.equals("west")) {
+						cc.setDockSide(1);
+						continue;
+					}
+
+					if (part.startsWith("hidemode ")) {
+						cc.setHideMode(Integer.parseInt(part.substring(9)));
+						continue;
+					}
+				}
+
+				if (c == 'i' && part.startsWith("id ")) {
+					cc.setId(part.substring(3).trim());
+					int dIx = cc.getId().indexOf('.');
+					if (dIx == 0 || dIx == cc.getId().length() - 1)
+						throw new IllegalArgumentException("Dot must not be first or last!");
+
+					continue;
+				}
+
+				if (c == 'e') {
+					if (part.equals("east")) {
+						cc.setDockSide(3);
+						continue;
+					}
+
+					if (part.equals("external")) {
+						cc.setExternal(true);
+						continue;
+					}
+
+					ix = startsWithLenient(part, new String[]{"endgroupx", "endgroupy", "egx", "egy"}, new int[]{-1, -1, -1, -1}, true);
+					if (ix > -1) {
+						String sg = part.substring(ix).trim();
+						char lc = part.charAt(ix - 1);
+						DimConstraint dc = (lc == 'x' ? cc.getHorizontal() : cc.getVertical());
+						dc.setEndGroup(sg);
+						continue;
+					}
+				}
+
+				if (c == 'd') {
+					if (part.equals("dock north")) {
+						cc.setDockSide(0);
+						continue;
+					}
+					if (part.equals("dock west")) {
+						cc.setDockSide(1);
+						continue;
+					}
+					if (part.equals("dock south")) {
+						cc.setDockSide(2);
+						continue;
+					}
+					if (part.equals("dock east")) {
+						cc.setDockSide(3);
+						continue;
+					}
+
+					if (part.equals("dock center")) {
+						cc.getHorizontal().setGrow(100f);
+						cc.getVertical().setGrow(100f);
+						cc.setPushX(100f);
+						cc.setPushY(100f);
+						continue;
+					}
+				}
+
+				UnitValue horAlign = parseAlignKeywords(part, true);
+				if (horAlign != null) {
+					cc.getHorizontal().setAlign(horAlign);
+					continue;
+				}
+
+				UnitValue verAlign = parseAlignKeywords(part, false);
+				if (verAlign != null) {
+					cc.getVertical().setAlign(verAlign);
+					continue;
+				}
+
+				throw new IllegalArgumentException("Unknown keyword.");
+
+			} catch (Exception ex) {
+				throw new IllegalArgumentException("Illegal Constraint: '" + part + "'\n" + ex.getMessage());
+			}
+		}
+
+//		cc = (CC) serializeTest(cc);
+
+		return cc;
+	}
+
+	/** Parses insets which consists of 1-4 <code>UnitValue</code>s.
+	 * @param s The string to parse. E.g. "10 10 10 10" or "20". If less than 4 groups the last will be used for the missing.
+	 * @param acceptPanel If "panel" and "dialog" should be accepted. They are used to access platform defaults.
+	 * @return An array of length 4 with the parsed insets.
+	 * @throws IllegalArgumentException if the parsing could not be done.
+	 */
+	public static UnitValue[] parseInsets(String s, boolean acceptPanel)
+	{
+		if (s.length() == 0 || s.equals("dialog") || s.equals("panel")) {
+			if (acceptPanel == false)
+				throw new IllegalAccessError("Insets now allowed: " + s + "\n");
+
+			boolean isPanel = s.startsWith("p");
+			UnitValue[] ins = new UnitValue[4];
+			for (int j = 0; j < 4; j++)
+				ins[j] = isPanel ? PlatformDefaults.getPanelInsets(j) : PlatformDefaults.getDialogInsets(j);
+
+			return ins;
+		} else {
+			String[] insS = toTrimmedTokens(s, ' ');
+			UnitValue[] ins = new UnitValue[4];
+			for (int j = 0; j < 4; j++) {
+				UnitValue insSz = parseUnitValue(insS[j < insS.length ? j : insS.length - 1], UnitValue.ZERO, j % 2 == 1);
+				ins[j] = insSz != null ? insSz : PlatformDefaults.getPanelInsets(j);
+			}
+			return ins;
+		}
+	}
+
+	/** Parses gaps.
+	 * @param s The string that contains gap information. Should start with "gap".
+	 * @return The gaps as specified in <code>s</code>. Indexed: <code>[top,left,bottom,right][min,pref,max]</code> or
+	 * [before,after][min,pref,max] if <code>oneDim</code> is true.
+	 */
+	private static BoundSize[] parseGaps(String s)
+	{
+		BoundSize[] ret = new BoundSize[4];
+
+		int ix = startsWithLenient(s, "gaptop", -1, true);
+		if (ix > -1) {
+			s = s.substring(ix).trim();
+			ret[0] = parseBoundSize(s, true, false);
+			return ret;
+		}
+
+		ix = startsWithLenient(s, "gapleft", -1, true);
+		if (ix > -1) {
+			s = s.substring(ix).trim();
+			ret[1] = parseBoundSize(s, true, true);
+			return ret;
+		}
+
+		ix = startsWithLenient(s, "gapbottom", -1, true);
+		if (ix > -1) {
+			s = s.substring(ix).trim();
+			ret[2] = parseBoundSize(s, true, false);
+			return ret;
+		}
+
+		ix = startsWithLenient(s, "gapright", -1, true);
+		if (ix > -1) {
+			s = s.substring(ix).trim();
+			ret[3] = parseBoundSize(s, true, true);
+			return ret;
+		}
+
+		ix = startsWithLenient(s, "gapbefore", -1, true);
+		if (ix > -1) {
+			s = s.substring(ix).trim();
+			ret[1] = parseBoundSize(s, true, true);
+			return ret;
+		}
+
+		ix = startsWithLenient(s, "gapafter", -1, true);
+		if (ix > -1) {
+			s = s.substring(ix).trim();
+			ret[3] = parseBoundSize(s, true, true);
+			return ret;
+		}
+
+		ix = startsWithLenient(s, new String[] {"gapx", "gapy"}, null, true);
+		if (ix > -1) {
+			boolean x = s.charAt(3) == 'x';
+			String[] gaps = toTrimmedTokens(s.substring(ix).trim(), ' ');
+			ret[x ? 1 : 0] = parseBoundSize(gaps[0], true, x);
+			if (gaps.length > 1)
+				ret[x ? 3 : 2] = parseBoundSize(gaps[1], true, !x);
+			return ret;
+		}
+
+		ix = startsWithLenient(s, "gap ", 1, true);
+		if (ix > -1) {
+			String[] gaps = toTrimmedTokens(s.substring(ix).trim(), ' ');
+
+			ret[1] = parseBoundSize(gaps[0], true, true);   // left
+			if (gaps.length > 1) {
+				ret[3] = parseBoundSize(gaps[1], true, false);   // right
+				if (gaps.length > 2) {
+					ret[0] = parseBoundSize(gaps[2], true, true);   // top
+					if (gaps.length > 3)
+						ret[2] = parseBoundSize(gaps[3], true, false);   // bottom
+				}
+			}
+			return ret;
+		}
+
+		throw new IllegalArgumentException("Unknown Gap part: '" + s + "'");
+	}
+
+	private static int parseSpan(String s)
+	{
+		return s.length() > 0 ? Integer.parseInt(s) : LayoutUtil.INF;
+	}
+
+	private static Float parseFloat(String s, Float nullVal)
+	{
+		return s.length() > 0 ? new Float(Float.parseFloat(s)) : nullVal;
+	}
+
+	/** Parses a single "min:pref:max" value. May look something like <code>"10px:20lp:30%"</code> or <code>"pref!"</code>.
+	 * @param s The string to parse. Not <code>null</code>.
+	 * @param isGap If this bound size is a gap (different empty string handling).
+	 * @param isHor If the size is for the horizontal dimension.
+	 * @return A bound size that may be <code>null</code> if the string was "null", "n" or <code>null</code>.
+	 */
+	public static BoundSize parseBoundSize(String s, boolean isGap, boolean isHor)
+	{
+		if (s.length() == 0 || s.equals("null") || s.equals("n"))
+			return null;
+
+		String cs = s;
+		boolean push = false;
+		if (s.endsWith("push")) {
+			push = true;
+			int l = s.length();
+			s = s.substring(0, l - (s.endsWith(":push") ? 5 : 4));
+			if (s.length() == 0)
+				return new BoundSize(null, null, null, true, cs);
+		}
+
+		String[] sizes = toTrimmedTokens(s, ':');
+		String s0 = sizes[0];
+
+		if (sizes.length == 1) {
+			boolean hasEM = s0.endsWith("!");
+			if (hasEM)
+				s0 = s0.substring(0, s0.length() - 1);
+			UnitValue uv = parseUnitValue(s0, null, isHor);
+			return new BoundSize(((isGap || hasEM) ? uv : null), uv, (hasEM ? uv : null), push, cs);
+
+		} else if (sizes.length == 2) {
+			return new BoundSize(parseUnitValue(s0, null, isHor), parseUnitValue(sizes[1], null, isHor), null, push, cs);
+		} else if (sizes.length == 3) {
+			return new BoundSize(parseUnitValue(s0, null, isHor), parseUnitValue(sizes[1], null, isHor), parseUnitValue(sizes[2], null, isHor), push, cs);
+		} else {
+			throw new IllegalArgumentException("Min:Preferred:Max size section must contain 0, 1 or 2 colons. '" + cs + "'");
+		}
+	}
+
+	/** Parses a single unit value that may also be an alignment as parsed by {@link #parseAlignKeywords(String, boolean)}.
+	 * @param s The string to parse. Not <code>null</code>. May look something like <code>"10px"</code> or <code>"5dlu"</code>.
+	 * @param isHor If the value is for the horizontal dimension.
+	 * @param emptyReplacement A replacement if <code>s</code> is empty. May be <code>null</code>.
+	 * @return The parsed unit value. May be <code>null</code>.
+	 */
+	public static UnitValue parseUnitValueOrAlign(String s, boolean isHor, UnitValue emptyReplacement)
+	{
+		if (s.length() == 0)
+			return emptyReplacement;
+
+		UnitValue align = parseAlignKeywords(s, isHor);
+		if (align != null)
+			return align;
+
+		return parseUnitValue(s, emptyReplacement, isHor);
+	}
+
+	/** Parses a single unit value. E.g. "10px" or "5in"
+	 * @param s The string to parse. Not <code>null</code>. May look something like <code>"10px"</code> or <code>"5dlu"</code>.
+	 * @param isHor If the value is for the horizontal dimension.
+	 * @return The parsed unit value. <code>null</code> is empty string,
+	 */
+	public static UnitValue parseUnitValue(String s, boolean isHor)
+	{
+		return parseUnitValue(s, null, isHor);
+	}
+
+	/** Parses a single unit value.
+	 * @param s The string to parse. May be <code>null</code>. May look something like <code>"10px"</code> or <code>"5dlu"</code>.
+	 * @param emptyReplacement A replacement <code>s</code> is empty or <code>null</code>. May be <code>null</code>.
+	 * @param isHor If the value is for the horizontal dimension.
+	 * @return The parsed unit value. May be <code>null</code>.
+	 */
+	private static UnitValue parseUnitValue(String s, UnitValue emptyReplacement, boolean isHor)
+	{
+		if (s == null || s.length() == 0)
+			return emptyReplacement;
+
+		String cs = s; // Save creation string.
+		char c0 = s.charAt(0);
+
+		// Remove start and end parentheses, if there.
+		if (c0 == '(' && s.charAt(s.length() - 1) == ')')
+			s = s.substring(1, s.length() - 1);
+
+		if (c0 == 'n' && (s.equals("null") || s.equals("n")))
+			return null;
+
+		if (c0 == 'i' && s.equals("inf"))
+			return UnitValue.INF;
+
+		int oper = getOper(s);
+		boolean inline = oper == UnitValue.ADD || oper == UnitValue.SUB || oper == UnitValue.MUL || oper == UnitValue.DIV;
+
+		if (oper != UnitValue.STATIC) {  // It is a multi-value
+
+			String[] uvs;
+			if (inline == false) {   // If the format is of type "opr(xxx,yyy)" (compared to in-line "10%+15px")
+				String sub = s.substring(4, s.length() - 1).trim();
+				uvs = toTrimmedTokens(sub, ',');
+				if (uvs.length == 1)
+					return parseUnitValue(sub, null, isHor);
+			} else {
+				char delim;
+				if (oper == UnitValue.ADD) {
+					delim = '+';
+				} else if (oper == UnitValue.SUB) {
+					delim = '-';
+				} else if (oper == UnitValue.MUL) {
+					delim = '*';
+				} else {    // div left
+					delim = '/';
+				}
+				uvs = toTrimmedTokens(s, delim);
+				if (uvs.length > 2) {   // More than one +-*/.
+					String last = uvs[uvs.length - 1];
+					String first = s.substring(0, s.length() - last.length() - 1);
+					uvs = new String[] {first, last};
+				}
+			}
+
+			if (uvs.length != 2)
+				throw new IllegalArgumentException("Malformed UnitValue: '" + s + "'");
+
+			UnitValue sub1 = parseUnitValue(uvs[0], null, isHor);
+			UnitValue sub2 = parseUnitValue(uvs[1], null, isHor);
+
+			if (sub1 == null || sub2 == null)
+				throw new IllegalArgumentException("Malformed UnitValue. Must be two sub-values: '" + s + "'");
+
+			return new UnitValue(isHor, oper, sub1, sub2, cs);
+		} else {
+			try {
+				String[] numParts = getNumTextParts(s);
+				float value = numParts[0].length() > 0 ? Float.parseFloat(numParts[0]) : 1;     // e.g. "related" has no number part..
+
+				return new UnitValue(value, numParts[1], isHor, oper, cs);
+
+			} catch(Exception e) {
+				throw new IllegalArgumentException("Malformed UnitValue: '" + s + "'");
+			}
+		}
+	}
+
+	/** Parses alignment keywords and returns the appropriate <code>UnitValue</code>.
+	 * @param s The string to parse. Not <code>null</code>.
+	 * @param isHor If alignments for horizontal is checked. <code>false</code> means vertical.
+	 * @return The unit value or <code>null</code> if not recognized (no exception).
+	 */
+	static UnitValue parseAlignKeywords(String s, boolean isHor)
+	{
+		if (startsWithLenient(s, "center", 1, false) != -1)
+			return UnitValue.CENTER;
+
+		if (isHor) {
+			if (startsWithLenient(s, "left", 1, false) != -1)
+				return UnitValue.LEFT;
+
+			if (startsWithLenient(s, "right", 1, false) != -1)
+				return UnitValue.RIGHT;
+
+			if (startsWithLenient(s, "leading", 4, false) != -1)
+				return UnitValue.LEADING;
+
+			if (startsWithLenient(s, "trailing", 5, false) != -1)
+				return UnitValue.TRAILING;
+
+			if (startsWithLenient(s, "label", 5, false) != -1)
+				return UnitValue.LABEL;
+
+		} else {
+
+			if (startsWithLenient(s, "baseline", 4, false) != -1)
+				return UnitValue.BASELINE_IDENTITY;
+
+			if (startsWithLenient(s, "top", 1, false) != -1)
+				return UnitValue.TOP;
+
+			if (startsWithLenient(s, "bottom", 1, false) != -1)
+				return UnitValue.BOTTOM;
+		}
+
+		return null;
+	}
+
+	/** Splits a text-number combination such as "hello 10.0" into <code>{"hello", "10.0"}</code>.
+	 * @param s The string to split. Not <code>null</code>. Needs be be reasonably formatted since the method
+	 * only finds the first 0-9 or . and cuts the string in half there.
+	 * @return Always length 2 and no <code>null</code> elements. Elements are "" if no part found.
+	 */
+	private static String[] getNumTextParts(String s)
+	{
+		for (int i = 0, iSz = s.length(); i < iSz; i++) {
+			char c = s.charAt(i);
+			if (c == ' ')
+				throw new IllegalArgumentException("Space in UnitValue: '" + s + "'");
+
+			if ((c < '0' || c > '9') && c != '.' && c != '-')
+				return new String[] {s.substring(0, i).trim(), s.substring(i).trim()};
+		}
+		return new String[] {s, ""};
+	}
+
+	/** Returns the operation depending on the start character.
+	 * @param s The string to check. Not <code>null</code>.
+	 * @return E.g. UnitValue.ADD, UnitValue.SUB or UnitValue.STATIC. Returns negative value for in-line operations.
+	 */
+	private static int getOper(String s)
+	{
+		int len = s.length();
+		if (len < 3)
+			return UnitValue.STATIC;
+
+		if (len > 5 && s.charAt(3) == '(' && s.charAt(len - 1) == ')') {
+			if (s.startsWith("min("))
+				return UnitValue.MIN;
+
+			if (s.startsWith("max("))
+				return UnitValue.MAX;
+
+			if (s.startsWith("mid("))
+				return UnitValue.MID;
+		}
+
+		// Try in-line add/sub. E.g. "pref+10px".
+		for (int j = 0; j < 2; j++) {   // First +-   then */   (precedence)
+			for (int i = len - 1, p = 0; i > 0; i--) {
+				char c = s.charAt(i);
+				if (c == ')') {
+					p++;
+				} else if (c == '(') {
+					p--;
+				} else if (p == 0) {
+					if (j == 0) {
+						if (c == '+')
+							return UnitValue.ADD;
+						if (c == '-')
+							return UnitValue.SUB;
+					} else {
+						if (c == '*')
+							return UnitValue.MUL;
+						if (c == '/')
+							return UnitValue.DIV;
+					}
+				}
+			}
+		}
+		return UnitValue.STATIC;
+	}
+
+	/** Returns if a string shares at least a specified numbers starting characters with a number of matches.
+	 * <p>
+	 * This method just exercise {@link #startsWithLenient(String, String, int, boolean)} with every one of
+	 * <code>matches</code> and <code>minChars</code>.
+	 * @param s The string to check. Not <code>null</code>.
+	 * @param matches A number of possible starts for <code>s</code>.
+	 * @param minChars The minimum number of characters to match for every element in <code>matches</code>. Needs
+	 * to be of same length as <code>matches</code>. Can be <code>null</code>.
+	 * @param acceptTrailing If after the required number of characters are matched on recognized characters that are not
+	 * in one of the the <code>matches</code> string should be accepted. For instance if "abczz" should be matched with
+	 * "abcdef" and min chars 3.
+	 * @return The index of the first unmatched character if <code>minChars</code> was reached or <code>-1</code> if a match was not
+	 * found.
+	 */
+	private static int startsWithLenient(String s, String[] matches, int[] minChars, boolean acceptTrailing)
+	{
+		for (int i = 0; i < matches.length; i++) {
+			int minChar = minChars != null ? minChars[i] : -1;
+			int ix = startsWithLenient(s, matches[i], minChar, acceptTrailing);
+			if (ix > -1)
+				return ix;
+		}
+		return -1;
+	}
+
+	/** Returns if a string shares at least a specified numbers starting characters with a match.
+	 * @param s The string to check. Not <code>null</code> and must be trimmed.
+	 * @param match The possible start for <code>s</code>. Not <code>null</code> and must be trimmed.
+	 * @param minChars The mimimum number of characters to match to <code>s</code> for it this to be considered a match. -1 means
+	 * the full length of <code>match</code>.
+	 * @param acceptTrailing If after the required number of charecters are matched unrecognized characters that are not
+	 * in one of the the <code>matches</code> string should be accepted. For instance if "abczz" should be matched with
+	 * "abcdef" and min chars 3.
+	 * @return The index of the first unmatched character if <code>minChars</code> was reached or <code>-1</code> if a match was not
+	 * found.
+	 */
+	private static int startsWithLenient(String s, String match, int minChars, boolean acceptTrailing)
+	{
+		if (s.charAt(0) != match.charAt(0)) // Fast sanity check.
+			return -1;
+
+		if (minChars == -1)
+			minChars = match.length();
+
+		int sSz = s.length();
+		if (sSz < minChars)
+			return -1;
+
+		int mSz = match.length();
+		int sIx = 0;
+		for (int mIx = 0; mIx < mSz; sIx++, mIx++) {
+			while (sIx < sSz && (s.charAt(sIx) == ' ' || s.charAt(sIx) == '_'))    // Disregard spaces and _
+				sIx++;
+
+			if (sIx >= sSz || s.charAt(sIx) != match.charAt(mIx))
+				return mIx >= minChars && (acceptTrailing || sIx >= sSz) && (sIx >= sSz || s.charAt(sIx - 1) == ' ') ? sIx : -1;
+		}
+		return sIx >= sSz || acceptTrailing ||s.charAt(sIx) == ' ' ? sIx : -1;
+	}
+
+	/** Parses a string and returns it in those parts of the string that are separated with a <code>sep</code> character.
+	 * <p>
+	 * separator characters within parentheses will not be counted or handled in any way, whatever the depth.
+	 * <p>
+	 * A space separator will be a hit to one or more spaces and thus not return empty strings.
+	 * @param s The string to parse. If it starts and/or ends with a <code>sep</code> the first and/or last element returned will be "". If
+	 * two <code>sep</code> are next to each other and empty element will be "between" the periods. The <code>sep</code> themselves will never be returned.
+	 * @param sep The separator char.
+	 * @return Those parts of the string that are separated with <code>sep</code>. Never null and at least of size 1
+	 * @since 6.7.2 Changed so more than one space in a row works as one space.
+	 */
+	private static String[] toTrimmedTokens(String s, char sep)
+	{
+		int toks = 0, sSize = s.length();
+		boolean disregardDoubles = sep == ' ';
+
+		// Count the sep:s
+		int p = 0;
+		for(int i = 0; i < sSize; i++) {
+			char c = s.charAt(i);
+			if (c == '(') {
+				p++;
+			} else if (c == ')') {
+				p--;
+			} else if (p == 0 && c == sep) {
+				toks++;
+				while (disregardDoubles && i < sSize - 1 && s.charAt(i + 1) == ' ')
+					i++;
+			}
+			if (p < 0)
+				throw new IllegalArgumentException("Unbalanced parentheses: '" + s + "'");
+		}
+		if (p != 0)
+			throw new IllegalArgumentException("Unbalanced parentheses: '" + s + "'");
+
+		if (toks == 0)
+			return new String [] {s.trim()};
+
+		String[] retArr = new String[toks + 1];
+
+		int st = 0, pNr = 0;
+		p = 0;
+		for (int i = 0; i < sSize; i++) {
+
+			char c = s.charAt(i);
+			if (c == '(') {
+				p++;
+			} else if (c == ')') {
+				p--;
+			} else if (p == 0 && c == sep) {
+				retArr[pNr++] = s.substring(st, i).trim();
+				st = i + 1;
+				while (disregardDoubles && i < sSize - 1 && s.charAt(i + 1) == ' ')
+					i++;
+			}
+		}
+
+		retArr[pNr++] = s.substring(st, sSize).trim();
+		return retArr;
+	}
+
+	/** Parses "AAA[BBB]CCC[DDD]EEE" into {"AAA", "BBB", "CCC", "DDD", "EEE", "FFF"}. Handles empty parts. Will always start and end outside
+	 * a [] block so that the number of returned elemets will always be uneven and at least of length 3.
+	 * <p>
+	 * "|" is interpreted as "][".
+	 * @param s The string. Might be "" but not null. Should be trimmed.
+	 * @return The string divided into elements. Never <code>null</code> and at least of length 3.
+	 * @throws IllegalArgumentException If a [] mismatch of some kind. (If not same [ as ] count or if the interleave.)
+	 */
+	private static ArrayList<String> getRowColAndGapsTrimmed(String s)
+	{
+		if (s.indexOf('|') != -1)
+			s = s.replaceAll("\\|", "][");
+
+		ArrayList<String> retList = new ArrayList<String>(Math.max(s.length() >> 2 + 1, 3)); // Approx return length.
+		int s0 = 0, s1 = 0; // '[' and ']' count.
+		int st = 0; // Start of "next token to add".
+		for (int i = 0, iSz = s.length(); i < iSz; i++) {
+			char c = s.charAt(i);
+			if (c == '[') {
+				s0++;
+			} else if (c == ']') {
+				s1++;
+			} else {
+				continue;
+			}
+
+			if (s0 != s1 && (s0 - 1) != s1)
+				break;  // Wrong [ or ] found. Break for throw.
+
+			retList.add(s.substring(st, i).trim());
+			st = i + 1;
+		}
+		if (s0 != s1)
+			throw new IllegalArgumentException("'[' and ']' mismatch in row/column format string: " + s);
+
+		if (s0 == 0) {
+			retList.add("");
+			retList.add(s);
+			retList.add("");
+		} else if (retList.size() % 2 == 0) {
+			retList.add(s.substring(st, s.length()));
+		}
+
+		return retList;
+	}
+
+	/** Makes <code>null</code> "", trims and converts to lower case.
+	 * @param s The string
+	 * @return Not null.
+	 */
+	public static String prepare(String s)
+	{
+		return s != null ? s.trim().toLowerCase() : "";
+	}
+
+//	/** Tests to serialize and deserialize the object with both XMLEncoder/Decoder and through Serializable
+//	 * @param o The object to serialize
+//	 * @return The same object after a tri through the process.
+//	 */
+//	public static final Object serializeTest(Object o)
+//	{
+//		try {
+//			ByteArrayOutputStream barr = new ByteArrayOutputStream();
+//			XMLEncoder enc = new XMLEncoder(barr);
+//			enc.writeObject(o);
+//			enc.close();
+//
+//			XMLDecoder dec = new XMLDecoder(new ByteArrayInputStream(barr.toByteArray()));
+//			o = dec.readObject();
+//			dec.close();
+//		} catch (Exception e) {
+//			e.printStackTrace();
+//		}
+//
+//		try {
+//			ByteArrayOutputStream barr = new ByteArrayOutputStream();
+//			ObjectOutputStream oos = new ObjectOutputStream(barr);
+//			oos.writeObject(o);
+//			oos.close();
+//
+//			ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
+//			o = ois.readObject();
+//			ois.close();
+//		} catch (Exception e) {
+//			e.printStackTrace();
+//		}
+//
+//		return o;
+//	}
+}
diff --git a/src/net/miginfocom/layout/ContainerWrapper.java b/src/net/miginfocom/layout/ContainerWrapper.java
new file mode 100644
index 0000000..65428ad
--- /dev/null
+++ b/src/net/miginfocom/layout/ContainerWrapper.java
@@ -0,0 +1,69 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A class that wraps a container that contains components.
+ */
+public interface ContainerWrapper extends ComponentWrapper
+{
+	/** Returns the components of the container that wrapper is wrapping.
+	 * @return The components of the container that wrapper is wrapping. Never <code>null</code>.
+	 */
+	public abstract ComponentWrapper[] getComponents();
+
+	/** Returns the number of components that this parent has.
+	 * @return The number of components that this parent has.
+	 */
+	public abstract int getComponentCount();
+
+	/** Returns the <code>LayoutHandler</code> (in Swing terms) that is handling the layout of this container.
+	 * If there exist no such class the method should return the same as {@link #getComponent()}, which is the
+	 * container itself.
+	 * @return The layout handler instance. Never <code>null</code>.
+	 */
+	public abstract Object getLayout();
+
+	/** Returns if this container is using left-to-right component ordering.
+	 * @return If this container is using left-to-right component ordering.
+	 */
+	public abstract boolean isLeftToRight();
+
+	/** Paints a cell to indicate where it is.
+	 * @param x The x coordinate to start the drwaing.
+	 * @param y The x coordinate to start the drwaing.
+	 * @param width The width to draw/fill
+	 * @param height The height to draw/fill
+	 */
+	public abstract void paintDebugCell(int x, int y, int width, int height);
+}
diff --git a/src/net/miginfocom/layout/DimConstraint.java b/src/net/miginfocom/layout/DimConstraint.java
new file mode 100644
index 0000000..e49cf5f
--- /dev/null
+++ b/src/net/miginfocom/layout/DimConstraint.java
@@ -0,0 +1,471 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A simple value holder for a constraint for one dimension.
+ */
+public final class DimConstraint implements Externalizable
+{
+	/** How this entity can be resized in the dimension that this constraint represents.
+	 */
+	final ResizeConstraint resize = new ResizeConstraint();
+
+	// Look at the properties' getter/setter methods for explanation
+
+	private String sizeGroup = null;            // A "context" compared with equals.
+
+	private BoundSize size = BoundSize.NULL_SIZE;     // Min, pref, max. Never null, but sizes can be null.
+
+	private BoundSize gapBefore = null, gapAfter = null;
+
+	private UnitValue align = null;
+
+
+	// **************  Only applicable on components! *******************
+
+	private String endGroup = null;            // A "context" compared with equals.
+
+
+	// **************  Only applicable on rows/columns! *******************
+
+	private boolean fill = false;
+
+	private boolean noGrid = false;
+
+	/** Empty constructor.
+	 */
+	public DimConstraint()
+	{
+	}
+
+	/** Returns the grow priority. Relative priority is used for determining which entities gets the extra space first.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The grow priority.
+	 */
+	public int getGrowPriority()
+	{
+		return resize.growPrio;
+	}
+
+	/** Sets the grow priority. Relative priority is used for determining which entities gets the extra space first.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The new grow priority.
+	 */
+	public void setGrowPriority(int p)
+	{
+		resize.growPrio = p;
+	}
+
+	/** Returns the grow weight.<p>
+	 * Grow weight is how flexible the entity should be, relative to other entities, when it comes to growing. <code>null</code> or
+	 * zero mean it will never grow. An entity that has twice the grow weight compared to another entity will get twice
+	 * as much of available space.
+	 * <p>
+	 * GrowWeight are only compared within the same GrowPrio.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current grow weight.
+	 */
+	public Float getGrow()
+	{
+		return resize.grow;
+	}
+
+	/** Sets the grow weight.<p>
+	 * Grow weight is how flexible the entity should be, relative to other entities, when it comes to growing. <code>null</code> or
+	 * zero mean it will never grow. An entity that has twice the grow weight compared to another entity will get twice
+	 * as much of available space.
+	 * <p>
+	 * GrowWeight are only compared within the same GrowPrio.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param weight The new grow weight.
+	 */
+	public void setGrow(Float weight)
+	{
+		resize.grow = weight;
+	}
+
+	/** Returns the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The shrink priority.
+	 */
+	public int getShrinkPriority()
+	{
+		return resize.shrinkPrio;
+	}
+
+	/** Sets the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param p The new shrink priority.
+	 */
+	public void setShrinkPriority(int p)
+	{
+		resize.shrinkPrio = p;
+	}
+
+	/** Returns the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+	 * Shrink weight is how flexible the entity should be, relative to other entities, when it comes to shrinking. <code>null</code> or
+	 * zero mean it will never shrink (default). An entity that has twice the shrink weight compared to another entity will get twice
+	 * as much of available space.
+	 * <p>
+	 * Shrink(Weight) are only compared within the same ShrinkPrio.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current shrink weight.
+	 */
+	public Float getShrink()
+	{
+		return resize.shrink;
+	}
+
+	/** Sets the shrink priority. Relative priority is used for determining which entities gets smaller first when space is scarce.
+	 * Shrink weight is how flexible the entity should be, relative to other entities, when it comes to shrinking. <code>null</code> or
+	 * zero mean it will never shrink (default). An entity that has twice the shrink weight compared to another entity will get twice
+	 * as much of available space.
+	 * <p>
+	 * Shrink(Weight) are only compared within the same ShrinkPrio.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param weight The new shrink weight.
+	 */
+	public void setShrink(Float weight)
+	{
+		resize.shrink = weight;
+	}
+
+	public UnitValue getAlignOrDefault(boolean isCols)
+	{
+		if (align != null)
+			return align;
+
+		if (isCols)
+			return UnitValue.LEADING;
+
+		return fill || PlatformDefaults.getDefaultRowAlignmentBaseline() == false ? UnitValue.CENTER : UnitValue.BASELINE_IDENTITY;
+	}
+
+	/** Returns the alignment used either as a default value for sub-entities or for this entity.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The alignment.
+	 */
+	public UnitValue getAlign()
+	{
+		return align;
+	}
+
+	/** Sets the alignment used wither as a default value for sub-entities or for this entity.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param uv The new shrink priority. E.g. {@link UnitValue#CENTER} or {@link net.miginfocom.layout.UnitValue#LEADING}.
+	 */
+	public void setAlign(UnitValue uv)
+	{
+		this.align = uv;
+	}
+
+	/** Returns the gap after this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+	 * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The gap after this entity
+	 */
+	public BoundSize getGapAfter()
+	{
+		return gapAfter;
+	}
+
+	/** Sets the gap after this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+	 * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The new gap.
+	 * @see net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean).
+	 */
+	public void setGapAfter(BoundSize size)
+	{
+		this.gapAfter = size;
+	}
+
+	boolean hasGapAfter()
+	{
+		return gapAfter != null && gapAfter.isUnset() == false;
+	}
+
+	boolean isGapAfterPush()
+	{
+		return gapAfter != null && gapAfter.getGapPush();
+	}
+
+	/** Returns the gap before this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+	 * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The gap before this entity
+	 */
+	public BoundSize getGapBefore()
+	{
+		return gapBefore;
+	}
+
+	/** Sets the gap before this entity. The gap is an empty space and can have a min/preferred/maximum size so that it can shrink and
+	 * grow depending on available space. Gaps are against other entities' edges and not against other entities' gaps.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The new gap.
+	 * @see net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean).
+	 */
+	public void setGapBefore(BoundSize size)
+	{
+		this.gapBefore = size;
+	}
+
+	boolean hasGapBefore()
+	{
+		return gapBefore != null && gapBefore.isUnset() == false;
+	}
+
+	boolean isGapBeforePush()
+	{
+		return gapBefore != null && gapBefore.getGapPush();
+	}
+
+	/** Returns the min/preferred/max size for the entity in the dimension that this object describes.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current size. Never <code>null</code> since v3.5.
+	 * @see net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean).
+	 */
+	public BoundSize getSize()
+	{
+		return size;
+	}
+
+	/** Sets the min/preferred/max size for the entity in the dimension that this object describes.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param size The new size. May be <code>null</code>.
+	 */
+	public void setSize(BoundSize size)
+	{
+		if (size != null)
+			size.checkNotLinked();
+		this.size = size;
+	}
+
+	/** Returns the size group that this entity should be in for the dimension that this object is describing.
+	 * If this constraint is in a size group that is specified here. <code>null</code> means no size group
+	 * and all other values are legal. Comparison with .equals(). Components/columnss/rows in the same size group
+	 * will have the same min/preferred/max size; that of the largest in the group for the first two and the
+	 * smallest for max.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current size group. May be <code>null</code>.
+	 */
+	public String getSizeGroup()
+	{
+		return sizeGroup;
+	}
+
+	/** Sets the size group that this entity should be in for the dimension that this object is describing.
+	 * If this constraint is in a size group that is specified here. <code>null</code> means no size group
+	 * and all other values are legal. Comparison with .equals(). Components/columnss/rows in the same size group
+	 * will have the same min/preferred/max size; that of the largest in the group for the first two and the
+	 * smallest for max.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s The new size group. <code>null</code> disables size grouping.
+	 */
+	public void setSizeGroup(String s)
+	{
+		sizeGroup = s;
+	}
+
+	// **************  Only applicable on components ! *******************
+
+	/** Returns the end group that this entity should be in for the demension that this object is describing.
+	 * If this constraint is in an end group that is specified here. <code>null</code> means no end group
+	 * and all other values are legal. Comparison with .equals(). Components in the same end group
+	 * will have the same end coordinate.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return The current end group. <code>null</code> may be returned.
+	 */
+	public String getEndGroup()
+	{
+		return endGroup;
+	}
+
+	/** Sets the end group that this entity should be in for the demension that this object is describing.
+	 * If this constraint is in an end group that is specified here. <code>null</code> means no end group
+	 * and all other values are legal. Comparison with .equals(). Components in the same end group
+	 * will have the same end coordinate.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s The new end group. <code>null</code> disables end grouping.
+	 */
+	public void setEndGroup(String s)
+	{
+		endGroup = s;
+	}
+
+	// **************  Not applicable on components below ! *******************
+
+	/** Returns if the component in the row/column that this constraint should default be grown in the same dimension that
+	 * this constraint represents (width for column and height for a row).
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return code>true</code> means that components should grow.
+	 */
+	public boolean isFill()
+	{
+		return fill;
+	}
+
+	/** Sets if the component in the row/column that this constraint should default be grown in the same dimension that
+	 * this constraint represents (width for column and height for a row).
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>true</code> means that components should grow.
+	 */
+	public void setFill(boolean b)
+	{
+		fill = b;
+	}
+
+	/** Returns if the row/column should default to flow and not to grid behaviour. This means that the whole row/column
+	 * will be one cell and all components will end up in that cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>true</code> means that the whole row/column should be one cell.
+	 */
+	public boolean isNoGrid()
+	{
+		return noGrid;
+	}
+
+	/** Sets if the row/column should default to flow and not to grid behaviour. This means that the whole row/column
+	 * will be one cell and all components will end up in that cell.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>true</code> means that the whole row/column should be one cell.
+	 */
+	public void setNoGrid(boolean b)
+	{
+		this.noGrid = b;
+	}
+
+	/** Returns the gaps as pixel values.
+	 * @param parent The parent. Used to get the pixel values.
+	 * @param defGap The default gap to use if there is no gap set on this object (i.e. it is null).
+	 * @param refSize The reference size used to get the pixel sizes.
+	 * @param before IF it is the gap before rather than the gap after to return.
+	 * @return The [min,preferred,max] sizes for the specified gap. Uses {@link net.miginfocom.layout.LayoutUtil#NOT_SET}
+	 * for gap sizes that are <code>null</code>. Returns <code>null</code> if there was no gap specified. A new and free to use array.
+	 */
+	int[] getRowGaps(ContainerWrapper parent, BoundSize defGap, int refSize, boolean before)
+	{
+		BoundSize gap = before ? gapBefore : gapAfter;
+		if (gap == null || gap.isUnset())
+			gap = defGap;
+
+		if (gap == null || gap.isUnset())
+			return null;
+
+		int[] ret = new int[3];
+		for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
+			UnitValue uv = gap.getSize(i);
+			ret[i] = uv != null ? uv.getPixels(refSize, parent, null) : LayoutUtil.NOT_SET;
+		}
+		return ret;
+	}
+
+	/** Returns the gaps as pixel values.
+	 * @param parent The parent. Used to get the pixel values.
+	 * @param comp The component that the gap is for. If not for a component it is <code>null</code>.
+	 * @param adjGap The gap that the adjacent component, if any, has towards <code>comp</code>.
+	 * @param adjacentComp The adjacent component if any. May be <code>null</code>.
+	 * @param refSize The reference size used to get the pixel sizes.
+	 * @param adjacentSide What side the <code>adjacentComp</code> is on. 0 = top, 1 = left, 2 = bottom, 3 = right.
+	 * @param tag The tag string that the component might be tagged with in the component constraints. May be <code>null</code>.
+	 * @param isLTR If it is left-to-right.
+	 * @return The [min,preferred,max] sizes for the specified gap. Uses {@link net.miginfocom.layout.LayoutUtil#NOT_SET}
+	 * for gap sizes that are <code>null</code>. Returns <code>null</code> if there was no gap specified. A new and free to use array.
+	 */
+	int[] getComponentGaps(ContainerWrapper parent, ComponentWrapper comp, BoundSize adjGap, ComponentWrapper adjacentComp, String tag, int refSize, int adjacentSide, boolean isLTR)
+	{
+		BoundSize gap = adjacentSide < 2 ? gapBefore : gapAfter;
+
+		boolean hasGap = gap != null && gap.getGapPush();
+		if ((gap == null || gap.isUnset()) && (adjGap == null || adjGap.isUnset()) && comp != null)
+			gap = PlatformDefaults.getDefaultComponentGap(comp, adjacentComp, adjacentSide + 1, tag, isLTR);
+
+		if (gap == null)
+			return hasGap ? new int[] {0, 0, LayoutUtil.NOT_SET} : null;
+
+		int[] ret = new int[3];
+		for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
+			UnitValue uv = gap.getSize(i);
+			ret[i] = uv != null ? uv.getPixels(refSize, parent, null) : LayoutUtil.NOT_SET;
+		}
+		return ret;
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+
+	public void writeExternal(ObjectOutput out) throws IOException
+	{
+		if (getClass() == DimConstraint.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+}
diff --git a/src/net/miginfocom/layout/Grid.java b/src/net/miginfocom/layout/Grid.java
new file mode 100644
index 0000000..5d2d469
--- /dev/null
+++ b/src/net/miginfocom/layout/Grid.java
@@ -0,0 +1,2353 @@
+package net.miginfocom.layout;
+
+
+import java.util.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** Holds components in a grid. Does most of the logic behind the layout manager.
+ */
+public final class Grid
+{
+	public static final boolean TEST_GAPS = true;
+
+	private static final Float[] GROW_100 = new Float[] {ResizeConstraint.WEIGHT_100};
+
+	private static final DimConstraint DOCK_DIM_CONSTRAINT = new DimConstraint();
+	static {
+		DOCK_DIM_CONSTRAINT.setGrowPriority(0);
+	}
+
+	/** This is the maximum grid position for "normal" components. Docking components use the space out to
+	 * <code>MAX_DOCK_GRID</code> and below 0.
+	 */
+	private static final int MAX_GRID = 30000;
+
+	/** Docking components will use the grid coordinates <code>-MAX_DOCK_GRID -> 0</code> and <code>MAX_GRID -> MAX_DOCK_GRID</code>.
+	 */
+	private static final int MAX_DOCK_GRID = 32767;
+
+	/** A constraint used for gaps.
+	 */
+	private static final ResizeConstraint GAP_RC_CONST = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, null);
+	private static final ResizeConstraint GAP_RC_CONST_PUSH = new ResizeConstraint(200, ResizeConstraint.WEIGHT_100, 50, ResizeConstraint.WEIGHT_100);
+
+	/** The constraints. Never <code>null</code>.
+	 */
+	private final LC lc;
+
+	/** The parent that is layout out and this grid is done for. Never <code>null</code>.
+	 */
+	private final ContainerWrapper container;
+
+	/** An x, y array implemented as a sparse array to accommodate for any grid size without wasting memory (or rather 15 bit (0-MAX_GRID * 0-MAX_GRID).
+	 */
+	private final LinkedHashMap<Integer, Cell> grid = new LinkedHashMap<Integer, Cell>();   // [(y << 16) + x] -> Cell. null key for absolute positioned compwraps
+
+	private HashMap<Integer, BoundSize> wrapGapMap = null;   // Row or Column index depending in the dimension that "wraps". Normally row indexes but may be column indexes if "flowy". 0 means before first row/col.
+
+	/** The size of the grid. Row count and column count.
+	 */
+	private final TreeSet<Integer> rowIndexes = new TreeSet<Integer>(), colIndexes = new TreeSet<Integer>();
+
+	/** The row and column specifications.
+	 */
+	private final AC rowConstr, colConstr;
+
+	/** The in the constructor calculated min/pref/max sizes of the rows and columns.
+	 */
+	private FlowSizeSpec colFlowSpecs = null, rowFlowSpecs = null;
+
+	/** Components that are connections in one dimension (such as baseline alignment for instance) are grouped together and stored here.
+	 * One for each row/column.
+	 */
+	private final ArrayList<LinkedDimGroup>[] colGroupLists, rowGroupLists;   //[(start)row/col number]
+
+	/** The in the constructor calculated min/pref/max size of the whole grid.
+	 */
+	private int[] width = null, height = null;
+
+	/** If debug is on contains the bounds for things to paint when calling {@link ContainerWrapper#paintDebugCell(int, int, int, int)}
+	  */
+	private ArrayList<int[]> debugRects = null; // [x, y, width, height]
+
+	/** If any of the absolute coordinates for component bounds has links the name of the target is in this Set.
+	 * Since it requires some memory and computations this is checked at the creation so that
+	 * the link information is only created if needed later.
+	 * <p>
+	 * The boolean is true for groups id:s and null for normal id:s.
+	 */
+	private HashMap<String, Boolean> linkTargetIDs = null;
+
+	private final int dockOffY, dockOffX;
+
+	private final Float[] pushXs, pushYs;
+
+	private final ArrayList<LayoutCallback> callbackList;
+
+	/** Constructor.
+	 * @param container The container that will be laid out.
+	 * @param lc The form flow constraints.
+	 * @param rowConstr The rows specifications. If more cell rows are required, the last element will be used for when there is no corresponding element in this array.
+	 * @param colConstr The columns specifications. If more cell rows are required, the last element will be used for when there is no corresponding element in this array.
+	 * @param ccMap The map containing the parsed constraints for each child component of <code>parent</code>. Will not be alterted.
+	 * @param callbackList A list of callbacks or <code>null</code> if none. Will not be alterted.
+	 */
+	public Grid(ContainerWrapper container, LC lc, AC rowConstr, AC colConstr, Map<ComponentWrapper, CC> ccMap, ArrayList<LayoutCallback> callbackList)
+	{
+		this.lc = lc;
+		this.rowConstr = rowConstr;
+		this.colConstr = colConstr;
+		this.container = container;
+		this.callbackList = callbackList;
+
+		int wrap = lc.getWrapAfter() != 0 ? lc.getWrapAfter() : (lc.isFlowX() ? colConstr : rowConstr).getConstaints().length;
+
+		final ComponentWrapper[] comps = container.getComponents();
+
+		boolean hasTagged = false;  // So we do not have to sort if it will not do any good
+		boolean hasPushX = false, hasPushY = false;
+		boolean hitEndOfRow = false;
+		final int[] cellXY = new int[2];
+		final ArrayList<int[]> spannedRects = new ArrayList<int[]>(2);
+
+		final DimConstraint[] specs = (lc.isFlowX() ? rowConstr : colConstr).getConstaints();
+
+		int sizeGroupsX = 0, sizeGroupsY = 0;
+		int[] dockInsets = null;    // top, left, bottom, right insets for docks.
+
+		LinkHandler.clearTemporaryBounds(container.getLayout());
+
+		for (int i = 0; i < comps.length;) {
+			ComponentWrapper comp = comps[i];
+			CC rootCc = getCC(comp, ccMap);
+
+			addLinkIDs(rootCc);
+
+			int hideMode = comp.isVisible() ? -1 : rootCc.getHideMode() != -1 ? rootCc.getHideMode() : lc.getHideMode();
+
+			if (hideMode == 3) { // To work with situations where there are components that does not have a layout manager, or not this one.
+				setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
+				i++;
+				continue;   // The "external" component should not be handled further.
+			}
+
+			if (rootCc.getHorizontal().getSizeGroup() != null)
+				sizeGroupsX++;
+			if (rootCc.getVertical().getSizeGroup() != null)
+				sizeGroupsY++;
+
+			// Special treatment of absolute positioned components.
+			UnitValue[] pos = getPos(comp, rootCc);
+			BoundSize[] cbSz = getCallbackSize(comp);
+			if (pos != null || rootCc.isExternal()) {
+
+				CompWrap cw = new CompWrap(comp, rootCc, hideMode, pos, cbSz);
+				Cell cell = grid.get(null);
+				if (cell == null) {
+					grid.put(null, new Cell(cw));
+				} else {
+					cell.compWraps.add(cw);
+				}
+
+				if (rootCc.isBoundsInGrid() == false || rootCc.isExternal()) {
+					setLinkedBounds(comp, rootCc, comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight(), rootCc.isExternal());
+					i++;
+					continue;
+				}
+			}
+
+			if (rootCc.getDockSide() != -1) {
+				if (dockInsets == null)
+					dockInsets = new int[] {-MAX_DOCK_GRID, -MAX_DOCK_GRID, MAX_DOCK_GRID, MAX_DOCK_GRID};
+
+				addDockingCell(dockInsets, rootCc.getDockSide(), new CompWrap(comp, rootCc, hideMode, pos, cbSz));
+				i++;
+				continue;
+			}
+
+			Boolean cellFlowX = rootCc.getFlowX();
+			Cell cell = null;
+
+			if (rootCc.isNewline()) {
+				wrap(cellXY, rootCc.getNewlineGapSize());
+			} else if (hitEndOfRow) {
+				wrap(cellXY, null);
+			}
+			hitEndOfRow = false;
+
+			final boolean rowNoGrid = lc.isNoGrid() || ((DimConstraint) LayoutUtil.getIndexSafe(specs, lc.isFlowX() ? cellXY[1] : cellXY[0])).isNoGrid();
+
+			// Move to a free y, x  if no absolute grid specified
+			int cx = rootCc.getCellX();
+			int cy = rootCc.getCellY();
+			if ((cx < 0 || cy < 0) && rowNoGrid == false && rootCc.getSkip() == 0) { // 3.7.2: If skip, don't find an empty cell first.
+				while (isCellFree(cellXY[1], cellXY[0], spannedRects) == false) {
+					if (Math.abs(increase(cellXY, 1)) >= wrap)
+						wrap(cellXY, null);
+				}
+			} else {
+				if (cx >= 0 && cy >= 0) {
+					if (cy >= 0) {
+						cellXY[0] = cx;
+						cellXY[1] = cy;
+					} else {    // Only one coordinate is specified. Use the current row (flowx) or column (flowy) to fill in.
+						if (lc.isFlowX()) {
+							cellXY[0] = cx;
+						} else {
+							cellXY[1] = cx;
+						}
+					}
+				}
+				cell = getCell(cellXY[1], cellXY[0]);   // Might be null
+			}
+
+			// Skip a number of cells. Changed for 3.6.1 to take wrap into account and thus "skip" to the next and possibly more rows.
+			for (int s = 0, skipCount = rootCc.getSkip(); s < skipCount; s++) {
+				do {
+					if (Math.abs(increase(cellXY, 1)) >= wrap)
+						wrap(cellXY, null);
+				} while (isCellFree(cellXY[1], cellXY[0], spannedRects) == false);
+			}
+
+			// If cell is not created yet, create it and set it.
+			if (cell == null) {
+				int spanx = Math.min(rowNoGrid && lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanX(), MAX_GRID - cellXY[0]);
+				int spany = Math.min(rowNoGrid && !lc.isFlowX() ? LayoutUtil.INF : rootCc.getSpanY(), MAX_GRID - cellXY[1]);
+
+				cell = new Cell(spanx, spany, cellFlowX != null ? cellFlowX : lc.isFlowX());
+
+				setCell(cellXY[1], cellXY[0], cell);
+
+				// Add a rectangle so we can know that spanned cells occupy more space.
+				if (spanx > 1 || spany > 1)
+					spannedRects.add(new int[] {cellXY[0], cellXY[1], spanx, spany});
+			}
+
+			// Add the one, or all, components that split the grid position to the same Cell.
+			boolean wrapHandled = false;
+			int splitLeft = rowNoGrid ? LayoutUtil.INF : rootCc.getSplit() - 1;
+			boolean splitExit = false;
+			final boolean spanRestOfRow = (lc.isFlowX() ? rootCc.getSpanX() : rootCc.getSpanY()) == LayoutUtil.INF;
+
+			for (; splitLeft >= 0 && i < comps.length; splitLeft--) {
+				ComponentWrapper compAdd = comps[i];
+				CC cc = getCC(compAdd, ccMap);
+
+				addLinkIDs(cc);
+
+				boolean visible = compAdd.isVisible();
+				hideMode = visible ? -1 : cc.getHideMode() != -1 ? cc.getHideMode() : lc.getHideMode();
+
+				if (cc.isExternal() || hideMode == 3) {
+					i++;
+					splitLeft++;    // Added for 3.5.5 so that these components does not "take" a split slot.
+					continue;       // To work with situations where there are components that does not have a layout manager, or not this one.
+				}
+
+				hasPushX |= (visible || hideMode > 1) && (cc.getPushX() != null);
+				hasPushY |= (visible || hideMode > 1) && (cc.getPushY() != null);
+
+				if (cc != rootCc) { // If not first in a cell
+					if (cc.isNewline() || cc.isBoundsInGrid() == false || cc.getDockSide() != -1)
+						break;
+
+					if (splitLeft > 0 && cc.getSkip() > 0) {
+						splitExit = true;
+						break;
+					}
+
+					pos = getPos(compAdd, cc);
+					cbSz = getCallbackSize(compAdd);
+				}
+
+				CompWrap cw = new CompWrap(compAdd, cc, hideMode, pos, cbSz);
+				cell.compWraps.add(cw);
+				cell.hasTagged |= cc.getTag() != null;
+				hasTagged |= cell.hasTagged;
+
+				if (cc != rootCc) {
+					if (cc.getHorizontal().getSizeGroup() != null)
+						sizeGroupsX++;
+					if (cc.getVertical().getSizeGroup() != null)
+						sizeGroupsY++;
+				}
+
+				i++;
+
+				if ((cc.isWrap() || (spanRestOfRow && splitLeft == 0))) {
+					if (cc.isWrap()) {
+						wrap(cellXY, cc.getWrapGapSize());
+					} else {
+						hitEndOfRow = true;
+					}
+					wrapHandled = true;
+					break;
+				}
+			}
+
+			if (wrapHandled == false && rowNoGrid == false) {
+				int span = lc.isFlowX() ? cell.spanx : cell.spany;
+				if (Math.abs((lc.isFlowX() ? cellXY[0] : cellXY[1])) + span >= wrap) {
+					hitEndOfRow = true;
+				} else {
+					increase(cellXY, splitExit ? span - 1 : span);
+				}
+			}
+		}
+
+		// If there were size groups, calculate the largest values in the groups (for min/pref/max) and enforce them on the rest in the group.
+		if (sizeGroupsX > 0 || sizeGroupsY > 0) {
+			HashMap<String, int[]> sizeGroupMapX = sizeGroupsX > 0 ? new HashMap<String, int[]>(sizeGroupsX) : null;
+			HashMap<String, int[]> sizeGroupMapY = sizeGroupsY > 0 ? new HashMap<String, int[]>(sizeGroupsY) : null;
+			ArrayList<CompWrap> sizeGroupCWs = new ArrayList<CompWrap>(Math.max(sizeGroupsX, sizeGroupsY));
+
+			for (Cell cell : grid.values()) {
+				for (int i = 0; i < cell.compWraps.size(); i++) {
+					CompWrap cw = cell.compWraps.get(i);
+					String sgx = cw.cc.getHorizontal().getSizeGroup();
+					String sgy = cw.cc.getVertical().getSizeGroup();
+
+					if (sgx != null || sgy != null) {
+						if (sgx != null && sizeGroupMapX != null)
+							addToSizeGroup(sizeGroupMapX, sgx, cw.horSizes);
+						if (sgy != null && sizeGroupMapY != null)
+							addToSizeGroup(sizeGroupMapY, sgy, cw.verSizes);
+						sizeGroupCWs.add(cw);
+					}
+				}
+			}
+
+			// Set/equalize the sizeGroups to same the values.
+			for (CompWrap cw : sizeGroupCWs) {
+				if (sizeGroupMapX != null)
+					cw.setSizes(sizeGroupMapX.get(cw.cc.getHorizontal().getSizeGroup()), true);  // Target method handles null sizes
+				if (sizeGroupMapY != null)
+					cw.setSizes(sizeGroupMapY.get(cw.cc.getVertical().getSizeGroup()), false); // Target method handles null sizes
+			}
+		} // Component loop
+
+		// If there were size groups, calculate the largest values in the groups (for min/pref/max) and enforce them on the rest in the group.
+		if (sizeGroupsX > 0 || sizeGroupsY > 0) {
+			HashMap<String, int[]> sizeGroupMapX = sizeGroupsX > 0 ? new HashMap<String, int[]>(sizeGroupsX) : null;
+			HashMap<String, int[]> sizeGroupMapY = sizeGroupsY > 0 ? new HashMap<String, int[]>(sizeGroupsY) : null;
+			ArrayList<CompWrap> sizeGroupCWs = new ArrayList<CompWrap>(Math.max(sizeGroupsX, sizeGroupsY));
+
+			for (Cell cell : grid.values()) {
+				for (int i = 0; i < cell.compWraps.size(); i++) {
+					CompWrap cw = cell.compWraps.get(i);
+					String sgx = cw.cc.getHorizontal().getSizeGroup();
+					String sgy = cw.cc.getVertical().getSizeGroup();
+
+					if (sgx != null || sgy != null) {
+						if (sgx != null && sizeGroupMapX != null)
+							addToSizeGroup(sizeGroupMapX, sgx, cw.horSizes);
+						if (sgy != null && sizeGroupMapY != null)
+							addToSizeGroup(sizeGroupMapY, sgy, cw.verSizes);
+						sizeGroupCWs.add(cw);
+					}
+				}
+			}
+
+			// Set/equalize the sizeGroups to same the values.
+			for (CompWrap cw : sizeGroupCWs) {
+				if (sizeGroupMapX != null)
+					cw.setSizes(sizeGroupMapX.get(cw.cc.getHorizontal().getSizeGroup()), true);  // Target method handles null sizes
+				if (sizeGroupMapY != null)
+					cw.setSizes(sizeGroupMapY.get(cw.cc.getVertical().getSizeGroup()), false); // Target method handles null sizes
+			}
+		}
+
+		if (hasTagged)
+			sortCellsByPlatform(grid.values(), container);
+
+		// Calculate gaps now that the cells are filled and we know all adjacent components.
+		boolean ltr = LayoutUtil.isLeftToRight(lc, container);
+		for (Cell cell : grid.values()) {
+			ArrayList<CompWrap> cws = cell.compWraps;
+
+			for (int i = 0, lastI = cws.size() - 1; i <= lastI; i++) {
+				CompWrap cw = cws.get(i);
+				ComponentWrapper cwBef = i > 0 ? cws.get(i - 1).comp : null;
+				ComponentWrapper cwAft = i < lastI ? cws.get(i + 1).comp : null;
+
+				String tag = getCC(cw.comp, ccMap).getTag();
+				CC ccBef = cwBef != null ? getCC(cwBef, ccMap) : null;
+				CC ccAft = cwAft != null ? getCC(cwAft, ccMap) : null;
+
+				cw.calcGaps(cwBef, ccBef, cwAft, ccAft, tag, cell.flowx, ltr);
+			}
+		}
+
+		dockOffX = getDockInsets(colIndexes);
+		dockOffY = getDockInsets(rowIndexes);
+
+		// Add synthetic indexes for empty rows and columns so they can get a size
+		for (int i = 0, iSz = rowConstr.getCount(); i < iSz; i++)
+			rowIndexes.add(i);
+		for (int i = 0, iSz = colConstr.getCount(); i < iSz; i++)
+			colIndexes.add(i);
+
+		colGroupLists = divideIntoLinkedGroups(false);
+		rowGroupLists = divideIntoLinkedGroups(true);
+
+		pushXs = hasPushX || lc.isFillX() ? getDefaultPushWeights(false) : null;
+		pushYs = hasPushY || lc.isFillY() ? getDefaultPushWeights(true) : null;
+
+		if (LayoutUtil.isDesignTime(container))
+			saveGrid(container, grid);
+	}
+
+	private static CC getCC(ComponentWrapper comp, Map<ComponentWrapper, CC> ccMap)
+	{
+		CC cc = ccMap.get(comp);
+		return cc != null ? cc : new CC();
+	}
+
+	private void addLinkIDs(CC cc)
+	{
+		String[] linkIDs = cc.getLinkTargets();
+		for (String linkID : linkIDs) {
+			if (linkTargetIDs == null)
+				linkTargetIDs = new HashMap<String, Boolean>();
+			linkTargetIDs.put(linkID, null);
+		}
+	}
+
+	/** If the container (parent) that this grid is laying out has changed its bounds, call this method to
+	 * clear any cached values.
+	 */
+	public void invalidateContainerSize()
+	{
+		colFlowSpecs = null;
+	}
+
+	/** Does the actual layout. Uses many values calculated in the constructor.
+	 * @param bounds The bounds to layout against. Normally that of the parent. [x, y, width, height].
+	 * @param alignX The alignment for the x-axis.
+	 * @param alignY The alignment for the y-axis.
+	 * @param debug If debug information should be saved in {@link #debugRects}.
+	 * @param checkPrefChange If a check should be done to see if the setting of any new bounds changes the preferred size
+	 * of a component.
+	 * @return If the layout has probably changed the preferred size and there is need for a new layout (normally only SWT).
+	 */
+	public boolean layout(int[] bounds, UnitValue alignX, UnitValue alignY, boolean debug, boolean checkPrefChange)
+	{
+		if (debug)
+			debugRects = new ArrayList<int[]>();
+
+		checkSizeCalcs();
+
+		resetLinkValues(true, true);
+
+		layoutInOneDim(bounds[2], alignX, false, pushXs);
+		layoutInOneDim(bounds[3], alignY, true, pushYs);
+
+		HashMap<String, Integer> endGrpXMap = null, endGrpYMap = null;
+		int compCount = container.getComponentCount();
+
+		// Transfer the calculated bound from the ComponentWrappers to the actual Components.
+		boolean layoutAgain = false;
+		if (compCount > 0) {
+			for (int j = 0; j < (linkTargetIDs != null ? 2 : 1); j++) {   // First do the calculations (maybe more than once) then set the bounds when done
+				boolean doAgain;
+				int count = 0;
+				do {
+					doAgain = false;
+					for (Cell cell : grid.values()) {
+						ArrayList<CompWrap> compWraps = cell.compWraps;
+						for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+							CompWrap cw = compWraps.get(i);
+
+							if (j == 0) {
+								doAgain |= doAbsoluteCorrections(cw, bounds);
+								if (doAgain == false) { // If we are going to do this again, do not bother this time around
+									if (cw.cc.getHorizontal().getEndGroup() != null)
+										endGrpXMap = addToEndGroup(endGrpXMap, cw.cc.getHorizontal().getEndGroup(), cw.x + cw.w);
+									if (cw.cc.getVertical().getEndGroup() != null)
+										endGrpYMap = addToEndGroup(endGrpYMap, cw.cc.getVertical().getEndGroup(), cw.y + cw.h);
+								}
+
+								// @since 3.7.2 Needed or absolute "pos" pointing to "visual" or "container" didn't work if
+								// their bounds changed during the layout cycle. At least not in SWT.
+								if (linkTargetIDs != null && (linkTargetIDs.containsKey("visual") || linkTargetIDs.containsKey("container")))
+									layoutAgain = true;
+							}
+
+							if (linkTargetIDs == null || j == 1) {
+								if (cw.cc.getHorizontal().getEndGroup() != null)
+									cw.w = endGrpXMap.get(cw.cc.getHorizontal().getEndGroup()) - cw.x;
+								if (cw.cc.getVertical().getEndGroup() != null)
+									cw.h = endGrpYMap.get(cw.cc.getVertical().getEndGroup()) - cw.y;
+
+								cw.x += bounds[0];
+								cw.y += bounds[1];
+								layoutAgain |= cw.transferBounds(checkPrefChange && !layoutAgain);
+
+								if (callbackList != null) {
+									for (LayoutCallback callback : callbackList)
+										callback.correctBounds(cw.comp);
+								}
+							}
+						}
+					}
+					clearGroupLinkBounds();
+					if (++count > ((compCount << 3) + 10)) {
+						System.err.println("Unstable cyclic dependency in absolute linked values!");
+						break;
+					}
+
+				} while (doAgain);
+			}
+		}
+
+		// Add debug shapes for the "cells". Use the CompWraps as base for inding the cells.
+		if (debug) {
+			for (Cell cell : grid.values()) {
+				ArrayList<CompWrap> compWraps = cell.compWraps;
+				for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+					CompWrap cw = compWraps.get(i);
+					LinkedDimGroup hGrp = getGroupContaining(colGroupLists, cw);
+					LinkedDimGroup vGrp = getGroupContaining(rowGroupLists, cw);
+
+					if (hGrp != null && vGrp != null)
+						debugRects.add(new int[]{hGrp.lStart + bounds[0] - (hGrp.fromEnd ? hGrp.lSize : 0), vGrp.lStart + bounds[1] - (vGrp.fromEnd ? vGrp.lSize : 0), hGrp.lSize, vGrp.lSize});
+				}
+			}
+		}
+		return layoutAgain;
+	}
+
+	public void paintDebug()
+	{
+		if (debugRects != null) {
+			container.paintDebugOutline();
+
+			ArrayList<int[]> painted = new ArrayList<int[]>();
+			for (int i = 0, iSz = debugRects.size(); i < iSz; i++) {
+				int[] r = debugRects.get(i);
+				if (painted.contains(r) == false) {
+					container.paintDebugCell(r[0], r[1], r[2], r[3]);
+					painted.add(r);
+				}
+			}
+
+			for (Cell cell : grid.values()) {
+				ArrayList<CompWrap> compWraps = cell.compWraps;
+				for (int i = 0, iSz = compWraps.size(); i < iSz; i++)
+					compWraps.get(i).comp.paintDebugOutline();
+			}
+		}
+	}
+
+	public ContainerWrapper getContainer()
+	{
+		return container;
+	}
+
+	public final int[] getWidth()
+	{
+		checkSizeCalcs();
+		return width.clone();
+	}
+
+	public final int[] getHeight()
+	{
+		checkSizeCalcs();
+		return height.clone();
+	}
+
+	private void checkSizeCalcs()
+	{
+		if (colFlowSpecs == null) {
+
+			colFlowSpecs = calcRowsOrColsSizes(true);
+			rowFlowSpecs = calcRowsOrColsSizes(false);
+
+			width = getMinPrefMaxSumSize(true);
+			height = getMinPrefMaxSumSize(false);
+
+			if (linkTargetIDs == null) {
+				resetLinkValues(false, true);
+			} else {
+				// This call makes some components flicker on SWT. They get their bounds changed twice since
+				// the change might affect the absolute size adjustment below. There's no way around this that
+				// I know of.
+				layout(new int[4], null, null, false, false);
+				resetLinkValues(false, false);
+			}
+
+			adjustSizeForAbsolute(true);
+			adjustSizeForAbsolute(false);
+		}
+	}
+
+	private UnitValue[] getPos(ComponentWrapper cw, CC cc)
+	{
+		UnitValue[] cbPos = null;
+		if (callbackList != null) {
+			for (int i = 0; i < callbackList.size() && cbPos == null; i++)
+				cbPos = callbackList.get(i).getPosition(cw);   // NOT a copy!
+		}
+
+		// If one is null, return the other (which many also be null)
+		UnitValue[] ccPos = cc.getPos();    // A copy!!
+		if (cbPos == null || ccPos == null)
+			return cbPos != null ? cbPos : ccPos;
+
+		// Merge
+		for (int i = 0; i < 4; i++) {
+			UnitValue cbUv = cbPos[i];
+			if (cbUv != null)
+				ccPos[i] = cbUv;
+		}
+
+		return ccPos;
+	}
+
+	private BoundSize[] getCallbackSize(ComponentWrapper cw)
+	{
+		if (callbackList != null) {
+			for (LayoutCallback callback : callbackList) {
+				BoundSize[] bs = callback.getSize(cw);   // NOT a copy!
+				if (bs != null)
+					return bs;
+			}
+		}
+		return null;
+	}
+
+	private static int getDockInsets(TreeSet<Integer> set)
+	{
+		int c = 0;
+		for (Integer i : set) {
+			if (i < -MAX_GRID) {
+				c++;
+			} else {
+				break;  // Since they are sorted we can break
+			}
+		}
+		return c;
+	}
+
+	/**
+	 * @param cw Never <code>null</code>.
+	 * @param cc Never <code>null</code>.
+	 * @param external The bounds should be stored even if they are not in {@link #linkTargetIDs}.
+	 * @return If a change has been made.
+	 */
+	private boolean setLinkedBounds(ComponentWrapper cw, CC cc, int x, int y, int w, int h, boolean external)
+	{
+		String id = cc.getId() != null ? cc.getId() : cw.getLinkId();
+		if (id == null)
+			return false;
+
+		String gid = null;
+		int grIx = id.indexOf('.');
+		if (grIx != -1 ) {
+			gid = id.substring(0, grIx);
+			id = id.substring(grIx + 1);
+		}
+
+		Object lay = container.getLayout();
+		boolean changed = false;
+		if (external || (linkTargetIDs != null && linkTargetIDs.containsKey(id)))
+			changed = LinkHandler.setBounds(lay, id, x, y, w, h, !external, false);
+
+		if (gid != null && (external || (linkTargetIDs != null && linkTargetIDs.containsKey(gid)))) {
+			if (linkTargetIDs == null)
+				linkTargetIDs = new HashMap<String, Boolean>(4);
+
+			linkTargetIDs.put(gid, Boolean.TRUE);
+			changed |= LinkHandler.setBounds(lay, gid, x, y, w, h, !external, true);
+		}
+
+		return changed;
+	}
+
+	/** Go to next cell.
+	 * @param p The point to increase
+	 * @param cnt How many cells to advance.
+	 * @return The new value in the "incresing" dimension.
+	 */
+	private int increase(int[] p, int cnt)
+	{
+		return lc.isFlowX() ? (p[0] += cnt) : (p[1] += cnt);
+	}
+
+	/** Wraps to the next row or column depending on if horizontal flow or vertical flow is used.
+	 * @param cellXY The point to wrap and thus set either x or y to 0 and increase the other one.
+	 * @param gapSize The gaps size specified in a "wrap XXX" or "newline XXX" or <code>null</code> if none.
+	 */
+	private void wrap(int[] cellXY, BoundSize gapSize)
+	{
+		boolean flowx = lc.isFlowX();
+		cellXY[0] = flowx ? 0 : cellXY[0] + 1;
+		cellXY[1] = flowx ? cellXY[1] + 1 : 0;
+
+		if (gapSize != null) {
+			if (wrapGapMap == null)
+				wrapGapMap = new HashMap<Integer, BoundSize>(8);
+
+			wrapGapMap.put(cellXY[flowx ? 1 : 0], gapSize);
+		}
+
+		// add the row/column so that the gap in the last row/col will not be removed.
+		if (flowx) {
+			rowIndexes.add(cellXY[1]);
+		} else {
+			colIndexes.add(cellXY[0]);
+		}
+	}
+
+	/** Sort components (normally buttons in a button bar) so they appear in the correct order.
+	 * @param cells The cells to sort.
+	 * @param parent The parent.
+	 */
+	private static void sortCellsByPlatform(Collection<Cell> cells, ContainerWrapper parent)
+	{
+		String order = PlatformDefaults.getButtonOrder();
+		String orderLo = order.toLowerCase();
+
+		int unrelSize = PlatformDefaults.convertToPixels(1, "u", true, 0, parent, null);
+
+		if (unrelSize == UnitConverter.UNABLE)
+			throw new IllegalArgumentException("'unrelated' not recognized by PlatformDefaults!");
+
+		int[] gapUnrel = new int[] {unrelSize, unrelSize, LayoutUtil.NOT_SET};
+		int[] flGap = new int[] {0, 0, LayoutUtil.NOT_SET};
+
+		for (Cell cell : cells) {
+			if (cell.hasTagged == false)
+				continue;
+
+			CompWrap prevCW = null;
+			boolean nextUnrel = false;
+			boolean nextPush = false;
+			ArrayList<CompWrap> sortedList = new ArrayList<CompWrap>(cell.compWraps.size());
+
+			for (int i = 0, iSz = orderLo.length(); i < iSz; i++) {
+				char c = orderLo.charAt(i);
+				if (c == '+' || c == '_') {
+					nextUnrel = true;
+					if (c == '+')
+						nextPush = true;
+				} else {
+					String tag = PlatformDefaults.getTagForChar(c);
+					if (tag != null) {
+						for (int j = 0, jSz = cell.compWraps.size(); j < jSz; j++) {
+							CompWrap cw = cell.compWraps.get(j);
+							if (tag.equals(cw.cc.getTag())) {
+								if (Character.isUpperCase(order.charAt(i))) {
+									int min = PlatformDefaults.getMinimumButtonWidth().getPixels(0, parent, cw.comp);
+									if (min > cw.horSizes[LayoutUtil.MIN])
+										cw.horSizes[LayoutUtil.MIN] = min;
+
+									correctMinMax(cw.horSizes);
+								}
+
+								sortedList.add(cw);
+
+								if (nextUnrel) {
+									(prevCW != null ? prevCW : cw).mergeGapSizes(gapUnrel, cell.flowx, prevCW == null);
+									if (nextPush) {
+										cw.forcedPushGaps = 1;
+										nextUnrel = false;
+										nextPush = false;
+									}
+								}
+
+								// "unknown" components will always get an Unrelated gap.
+								if (c == 'u')
+									nextUnrel = true;
+								prevCW = cw;
+							}
+						}
+					}
+				}
+			}
+
+			// If we have a gap that was supposed to push but no more components was found to but the "gap before" then compensate.
+			if (sortedList.size() > 0) {
+				CompWrap cw = sortedList.get(sortedList.size() - 1);
+				if (nextUnrel) {
+					cw.mergeGapSizes(gapUnrel, cell.flowx, false);
+					if (nextPush)
+						cw.forcedPushGaps |= 2;
+				}
+
+				// Remove first and last gap if not set explicitly.
+				if (cw.cc.getHorizontal().getGapAfter() == null)
+					cw.setGaps(flGap, 3);
+
+				cw = sortedList.get(0);
+				if (cw.cc.getHorizontal().getGapBefore() == null)
+					cw.setGaps(flGap, 1);
+			}
+
+			// Exchange the unsorted CompWraps for the sorted one.
+			if (cell.compWraps.size() == sortedList.size()) {
+				cell.compWraps.clear();
+			} else {
+				cell.compWraps.removeAll(sortedList);
+			}
+			cell.compWraps.addAll(sortedList);
+		}
+	}
+
+	private Float[] getDefaultPushWeights(boolean isRows)
+	{
+		ArrayList<LinkedDimGroup>[] groupLists = isRows ? rowGroupLists : colGroupLists;
+
+		Float[] pushWeightArr = GROW_100;   // Only create specific if any of the components have grow.
+		for (int i = 0, ix = 1; i < groupLists.length; i++, ix += 2) {
+			ArrayList<LinkedDimGroup> grps = groupLists[i];
+			Float rowPushWeight = null;
+			for (LinkedDimGroup grp : grps) {
+				for (int c = 0; c < grp._compWraps.size(); c++) {
+					CompWrap cw = grp._compWraps.get(c);
+					int hideMode = cw.comp.isVisible() ? -1 : cw.cc.getHideMode() != -1 ? cw.cc.getHideMode() : lc.getHideMode();
+
+					Float pushWeight = hideMode < 2 ? (isRows ? cw.cc.getPushY() : cw.cc.getPushX()) : null;
+					if (rowPushWeight == null || (pushWeight != null && pushWeight.floatValue() > rowPushWeight.floatValue()))
+						rowPushWeight = pushWeight;
+				}
+			}
+
+			if (rowPushWeight != null) {
+				if (pushWeightArr == GROW_100)
+					pushWeightArr = new Float[(groupLists.length << 1) + 1];
+				pushWeightArr[ix] = rowPushWeight;
+			}
+		}
+
+		return pushWeightArr;
+	}
+
+	private void clearGroupLinkBounds()
+	{
+		if (linkTargetIDs == null)
+			return;
+
+		for (Map.Entry<String, Boolean> o : linkTargetIDs.entrySet()) {
+			if (o.getValue() == Boolean.TRUE)
+				LinkHandler.clearBounds(container.getLayout(), o.getKey());
+		}
+	}
+
+	private void resetLinkValues(boolean parentSize, boolean compLinks)
+	{
+		Object lay = container.getLayout();
+		if (compLinks)
+			LinkHandler.clearTemporaryBounds(lay);
+
+		boolean defIns = !hasDocks();
+
+		int parW = parentSize ? lc.getWidth().constrain(container.getWidth(), getParentSize(container, true), container) : 0;
+		int parH = parentSize ? lc.getHeight().constrain(container.getHeight(), getParentSize(container, false), container) : 0;
+
+		int insX = LayoutUtil.getInsets(lc, 0, defIns).getPixels(0, container, null);
+		int insY = LayoutUtil.getInsets(lc, 1, defIns).getPixels(0, container, null);
+		int visW = parW - insX - LayoutUtil.getInsets(lc, 2, defIns).getPixels(0, container, null);
+		int visH = parH - insY - LayoutUtil.getInsets(lc, 3, defIns).getPixels(0, container, null);
+
+		LinkHandler.setBounds(lay, "visual", insX, insY, visW, visH, true, false);
+		LinkHandler.setBounds(lay, "container", 0, 0, parW, parH, true, false);
+	}
+
+	/** Returns the {@link net.miginfocom.layout.Grid.LinkedDimGroup} that has the {@link net.miginfocom.layout.Grid.CompWrap}
+	 * <code>cw</code>.
+	 * @param groupLists The lists to search in.
+	 * @param cw The component wrap to find.
+	 * @return The linked group or <code>null</code> if none had the component wrap.
+	 */
+	private static LinkedDimGroup getGroupContaining(ArrayList<LinkedDimGroup>[] groupLists, CompWrap cw)
+	{
+		for (ArrayList<LinkedDimGroup> groups : groupLists) {
+			for (int j = 0, jSz = groups.size(); j < jSz; j++) {
+				ArrayList<CompWrap> cwList = groups.get(j)._compWraps;
+				for (int k = 0, kSz = cwList.size(); k < kSz; k++) {
+					if (cwList.get(k) == cw)
+						return groups.get(j);
+				}
+			}
+		}
+		return null;
+	}
+
+	private boolean doAbsoluteCorrections(CompWrap cw, int[] bounds)
+	{
+		boolean changed = false;
+
+		int[] stSz = getAbsoluteDimBounds(cw, bounds[2], true);
+		if (stSz != null)
+			cw.setDimBounds(stSz[0], stSz[1], true);
+
+		stSz = getAbsoluteDimBounds(cw, bounds[3], false);
+		if (stSz != null)
+			cw.setDimBounds(stSz[0], stSz[1], false);
+
+		// If there is a link id, store the new bounds.
+		if (linkTargetIDs != null)
+			changed = setLinkedBounds(cw.comp, cw.cc, cw.x, cw.y, cw.w, cw.h, false);
+
+		return changed;
+	}
+
+	private void adjustSizeForAbsolute(boolean isHor)
+	{
+		int[] curSizes = isHor ? width : height;
+
+		Cell absCell = grid.get(null);
+		if (absCell == null || absCell.compWraps.size() == 0)
+			return;
+
+		ArrayList<CompWrap> cws = absCell.compWraps;
+
+		int maxEnd = 0;
+		for (int j = 0, cwSz = absCell.compWraps.size(); j < cwSz + 3; j++) {  // "Do Again" max absCell.compWraps.size() + 3 times.
+			boolean doAgain = false;
+			for (int i = 0; i < cwSz; i++) {
+				CompWrap cw = cws.get(i);
+				int[] stSz = getAbsoluteDimBounds(cw, 0, isHor);
+				int end = stSz[0] + stSz[1];
+				if (maxEnd < end)
+					maxEnd = end;
+
+				// If there is a link id, store the new bounds.
+				if (linkTargetIDs != null)
+					doAgain |= setLinkedBounds(cw.comp, cw.cc, stSz[0], stSz[0], stSz[1], stSz[1], false);
+			}
+			if (doAgain == false)
+				break;
+
+			// We need to check this again since the coords may be smaller this round.
+			maxEnd = 0;
+			clearGroupLinkBounds();
+		}
+
+		maxEnd += LayoutUtil.getInsets(lc, isHor ? 3 : 2, !hasDocks()).getPixels(0, container, null);
+
+		if (curSizes[LayoutUtil.MIN] < maxEnd)
+			curSizes[LayoutUtil.MIN] = maxEnd;
+		if (curSizes[LayoutUtil.PREF] < maxEnd)
+			curSizes[LayoutUtil.PREF] = maxEnd;
+	}
+
+	private int[] getAbsoluteDimBounds(CompWrap cw, int refSize, boolean isHor)
+	{
+		if (cw.cc.isExternal()) {
+			if (isHor) {
+				return new int[] {cw.comp.getX(), cw.comp.getWidth()};
+			} else {
+				return new int[] {cw.comp.getY(), cw.comp.getHeight()};
+			}
+		}
+
+		int[] plafPad = lc.isVisualPadding() ? cw.comp.getVisualPadding() : null;
+		UnitValue[] pad = cw.cc.getPadding();
+
+		// If no changes do not create a lot of objects
+		if (cw.pos == null && plafPad == null && pad == null)
+			return null;
+
+		// Set start
+		int st = isHor ? cw.x : cw.y;
+		int sz = isHor ? cw.w : cw.h;
+
+		// If absolute, use those coordinates instead.
+		if (cw.pos != null) {
+			UnitValue stUV = cw.pos != null ? cw.pos[isHor ? 0 : 1] : null;
+			UnitValue endUV = cw.pos != null ? cw.pos[isHor ? 2 : 3] : null;
+
+			int minSz = cw.getSize(LayoutUtil.MIN, isHor);
+			int maxSz = cw.getSize(LayoutUtil.MAX, isHor);
+			sz = Math.min(Math.max(cw.getSize(LayoutUtil.PREF, isHor), minSz), maxSz);
+
+			if (stUV != null) {
+				st = stUV.getPixels(stUV.getUnit() == UnitValue.ALIGN ? sz : refSize, container, cw.comp);
+
+				if (endUV != null)  // if (endUV == null && cw.cc.isBoundsIsGrid() == true)
+					sz = Math.min(Math.max((isHor ? (cw.x + cw.w) : (cw.y + cw.h)) - st, minSz), maxSz);
+			}
+
+			if (endUV != null) {
+				if (stUV != null) {   // if (stUV != null || cw.cc.isBoundsIsGrid()) {
+					sz = Math.min(Math.max(endUV.getPixels(refSize, container, cw.comp) - st, minSz), maxSz);
+				} else {
+					st = endUV.getPixels(refSize, container, cw.comp) - sz;
+				}
+			}
+		}
+
+		// If constraint has padding -> correct the start/size
+		if (pad != null) {
+			UnitValue uv = pad[isHor ? 1 : 0];
+			int p = uv != null ? uv.getPixels(refSize, container, cw.comp) : 0;
+			st += p;
+			uv = pad[isHor ? 3 : 2];
+			sz += -p + (uv != null ? uv.getPixels(refSize, container, cw.comp) : 0);
+		}
+
+		// If the plaf converter has padding -> correct the start/size
+		if (plafPad != null) {
+			int p = plafPad[isHor ? 1 : 0];
+			st += p;
+			sz += -p + (plafPad[isHor ? 3 : 2]);
+		}
+
+		return new int[] {st, sz};
+	}
+
+	private void layoutInOneDim(int refSize, UnitValue align, boolean isRows, Float[] defaultPushWeights)
+	{
+		boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
+		DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
+		FlowSizeSpec fss = isRows ? rowFlowSpecs : colFlowSpecs;
+		ArrayList<LinkedDimGroup>[] rowCols = isRows ? rowGroupLists : colGroupLists;
+
+		int[] rowColSizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, defaultPushWeights, LayoutUtil.PREF, refSize);
+
+		if (LayoutUtil.isDesignTime(container)) {
+			TreeSet<Integer> indexes = isRows ? rowIndexes : colIndexes;
+			int[] ixArr = new int[indexes.size()];
+			int ix = 0;
+			for (Integer i : indexes)
+				ixArr[ix++] = i;
+
+			putSizesAndIndexes(container.getComponent(), rowColSizes, ixArr, isRows);
+		}
+
+		int curPos = align != null ? align.getPixels(refSize - LayoutUtil.sum(rowColSizes), container, null) : 0;
+
+		if (fromEnd)
+			curPos = refSize - curPos;
+
+		for (int i = 0 ; i < rowCols.length; i++) {
+			ArrayList<LinkedDimGroup> linkedGroups = rowCols[i];
+			int scIx = i - (isRows ? dockOffY : dockOffX);
+
+			int bIx = i << 1;
+			int bIx2 = bIx + 1;
+
+			curPos += (fromEnd ? -rowColSizes[bIx] : rowColSizes[bIx]);
+
+			DimConstraint primDC = scIx >= 0 ? primDCs[scIx >= primDCs.length ? primDCs.length - 1 : scIx] : DOCK_DIM_CONSTRAINT;
+
+			int rowSize = rowColSizes[bIx2];
+
+			for (LinkedDimGroup group : linkedGroups) {
+				int groupSize = rowSize;
+				if (group.span > 1)
+					groupSize = LayoutUtil.sum(rowColSizes, bIx2, Math.min((group.span << 1) - 1, rowColSizes.length - bIx2 - 1));
+
+				group.layout(primDC, curPos, groupSize, group.span);
+			}
+
+			curPos += (fromEnd ? -rowSize : rowSize);
+		}
+	}
+
+	private static void addToSizeGroup(HashMap<String, int[]> sizeGroups, String sizeGroup, int[] size)
+	{
+		int[] sgSize = sizeGroups.get(sizeGroup);
+		if (sgSize == null) {
+			sizeGroups.put(sizeGroup, new int[] {size[LayoutUtil.MIN], size[LayoutUtil.PREF], size[LayoutUtil.MAX]});
+		} else {
+			sgSize[LayoutUtil.MIN] = Math.max(size[LayoutUtil.MIN], sgSize[LayoutUtil.MIN]);
+			sgSize[LayoutUtil.PREF] = Math.max(size[LayoutUtil.PREF], sgSize[LayoutUtil.PREF]);
+			sgSize[LayoutUtil.MAX] = Math.min(size[LayoutUtil.MAX], sgSize[LayoutUtil.MAX]);
+		}
+	}
+
+	private static HashMap<String, Integer> addToEndGroup(HashMap<String, Integer> endGroups, String endGroup, int end)
+	{
+		if (endGroup != null) {
+			if (endGroups == null)
+				endGroups = new HashMap<String, Integer>(2);
+
+			Integer oldEnd = endGroups.get(endGroup);
+			if (oldEnd == null || end > oldEnd)
+				endGroups.put(endGroup, end);
+		}
+		return endGroups;
+	}
+
+	/** Calculates Min, Preferred and Max size for the columns OR rows.
+	 * @param isHor If it is the horizontal dimension to calculate.
+	 * @return The sizes in a {@link net.miginfocom.layout.Grid.FlowSizeSpec}.
+	 */
+	private FlowSizeSpec calcRowsOrColsSizes(boolean isHor)
+	{
+		ArrayList<LinkedDimGroup>[] groupsLists = isHor ? colGroupLists : rowGroupLists;
+		Float[] defPush = isHor ? pushXs : pushYs;
+		int refSize = isHor ? container.getWidth() : container.getHeight();
+
+		BoundSize cSz = isHor ? lc.getWidth() : lc.getHeight();
+		if (cSz.isUnset() == false)
+			refSize = cSz.constrain(refSize, getParentSize(container, isHor), container);
+
+		DimConstraint[] primDCs = (isHor? colConstr : rowConstr).getConstaints();
+		TreeSet<Integer> primIndexes = isHor ? colIndexes : rowIndexes;
+
+		int[][] rowColBoundSizes = new int[primIndexes.size()][];
+		HashMap<String, int[]> sizeGroupMap = new HashMap<String, int[]>(2);
+		DimConstraint[] allDCs = new DimConstraint[primIndexes.size()];
+
+		Iterator<Integer> primIt = primIndexes.iterator();
+		for (int r = 0; r < rowColBoundSizes.length; r++) {
+			int cellIx = primIt.next();
+			int[] rowColSizes = new int[3];
+
+			if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID) {  // If not dock cell
+				allDCs[r] = primDCs[cellIx >= primDCs.length ? primDCs.length - 1 : cellIx];
+			} else {
+				allDCs[r] = DOCK_DIM_CONSTRAINT;
+			}
+
+			ArrayList<LinkedDimGroup> groups = groupsLists[r];
+
+			int[] groupSizes = new int[] {
+					getTotalGroupsSizeParallel(groups, LayoutUtil.MIN, false),
+					getTotalGroupsSizeParallel(groups, LayoutUtil.PREF, false),
+					LayoutUtil.INF};
+
+			correctMinMax(groupSizes);
+			BoundSize dimSize = allDCs[r].getSize();
+
+			for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
+
+				int rowColSize = groupSizes[sType];
+
+				UnitValue uv = dimSize.getSize(sType);
+				if (uv != null) {
+					// If the size of the column is a link to some other size, use that instead
+					int unit = uv.getUnit();
+					if (unit == UnitValue.PREF_SIZE) {
+						rowColSize = groupSizes[LayoutUtil.PREF];
+					} else if (unit == UnitValue.MIN_SIZE) {
+						rowColSize = groupSizes[LayoutUtil.MIN];
+					} else if (unit == UnitValue.MAX_SIZE) {
+						rowColSize = groupSizes[LayoutUtil.MAX];
+					} else {
+						rowColSize = uv.getPixels(refSize, container, null);
+					}
+				} else if (cellIx >= -MAX_GRID && cellIx <= MAX_GRID && rowColSize == 0) {
+					rowColSize = LayoutUtil.isDesignTime(container) ? LayoutUtil.getDesignTimeEmptySize() : 0;    // Empty rows with no size set gets XX pixels if design time
+				}
+
+				rowColSizes[sType] = rowColSize;
+			}
+
+			correctMinMax(rowColSizes);
+			addToSizeGroup(sizeGroupMap, allDCs[r].getSizeGroup(), rowColSizes);
+
+			rowColBoundSizes[r] = rowColSizes;
+		}
+
+		// Set/equalize the size groups to same the values.
+		if (sizeGroupMap.size() > 0) {
+			for (int r = 0; r < rowColBoundSizes.length; r++) {
+				if (allDCs[r].getSizeGroup() != null)
+					rowColBoundSizes[r] = sizeGroupMap.get(allDCs[r].getSizeGroup());
+			}
+		}
+
+		// Add the gaps
+		ResizeConstraint[] resConstrs = getRowResizeConstraints(allDCs);
+
+		boolean[] fillInPushGaps = new boolean[allDCs.length + 1];
+		int[][] gapSizes = getRowGaps(allDCs, refSize, isHor, fillInPushGaps);
+
+		FlowSizeSpec fss = mergeSizesGapsAndResConstrs(resConstrs, fillInPushGaps, rowColBoundSizes, gapSizes);
+
+		// Spanning components are not handled yet. Check and adjust the multi-row min/pref they enforce.
+		adjustMinPrefForSpanningComps(allDCs, defPush, fss, groupsLists);
+
+		return fss;
+	}
+
+	private static int getParentSize(ComponentWrapper cw, boolean isHor)
+	{
+		ComponentWrapper p = cw.getParent();
+		return p != null ? (isHor ? cw.getWidth() : cw.getHeight()) : 0;
+	}
+
+	private int[] getMinPrefMaxSumSize(boolean isHor)
+	{
+		int[][] sizes = isHor ? colFlowSpecs.sizes : rowFlowSpecs.sizes;
+
+		int[] retSizes = new int[3];
+
+		BoundSize sz = isHor ? lc.getWidth() : lc.getHeight();
+
+		for (int i = 0; i < sizes.length; i++) {
+			if (sizes[i] != null) {
+				int[] size = sizes[i];
+				for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.MAX; sType++) {
+					if (sz.getSize(sType) != null) {
+						if (i == 0)
+							retSizes[sType] = sz.getSize(sType).getPixels(getParentSize(container, isHor), container, null);
+					} else {
+						int s = size[sType];
+
+						if (s != LayoutUtil.NOT_SET) {
+							if (sType == LayoutUtil.PREF) {
+								int bnd = size[LayoutUtil.MAX];
+								if (bnd != LayoutUtil.NOT_SET && bnd < s)
+									s = bnd;
+
+								bnd = size[LayoutUtil.MIN];
+								if (bnd > s)    // Includes s == LayoutUtil.NOT_SET since < 0.
+									s = bnd;
+							}
+
+							retSizes[sType] += s;   // MAX compensated below.
+						}
+
+						// So that MAX is always correct.
+						if (size[LayoutUtil.MAX] == LayoutUtil.NOT_SET || retSizes[LayoutUtil.MAX] > LayoutUtil.INF)
+							retSizes[LayoutUtil.MAX] = LayoutUtil.INF;
+					}
+				}
+			}
+		}
+
+		correctMinMax(retSizes);
+
+		return retSizes;
+	}
+
+	private static ResizeConstraint[] getRowResizeConstraints(DimConstraint[] specs)
+	{
+		ResizeConstraint[] resConsts = new ResizeConstraint[specs.length];
+		for (int i = 0; i < resConsts.length; i++)
+			resConsts[i] = specs[i].resize;
+		return resConsts;
+	}
+
+	private static ResizeConstraint[] getComponentResizeConstraints(ArrayList<CompWrap> compWraps, boolean isHor)
+	{
+		ResizeConstraint[] resConsts = new ResizeConstraint[compWraps.size()];
+		for (int i = 0; i < resConsts.length; i++) {
+			CC fc = compWraps.get(i).cc;
+			resConsts[i] = fc.getDimConstraint(isHor).resize;
+
+			// Always grow docking components in the correct dimension.
+			int dock = fc.getDockSide();
+			if (isHor ? (dock == 0 || dock == 2) : (dock == 1 || dock == 3)) {
+				ResizeConstraint dc = resConsts[i];
+				resConsts[i] = new ResizeConstraint(dc.shrinkPrio, dc.shrink, dc.growPrio, ResizeConstraint.WEIGHT_100);
+			}
+		}
+		return resConsts;
+	}
+
+	private static boolean[] getComponentGapPush(ArrayList<CompWrap> compWraps, boolean isHor)
+	{
+		// Make one element bigger and or the after gap with the next before gap.
+		boolean[] barr = new boolean[compWraps.size() + 1];
+		for (int i = 0; i < barr.length; i++) {
+
+			boolean push = i > 0 && compWraps.get(i - 1).isPushGap(isHor, false);
+
+			if (push == false && i < (barr.length - 1))
+				push = compWraps.get(i).isPushGap(isHor, true);
+
+			barr[i] = push;
+		}
+		return barr;
+	}
+
+	/** Returns the row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
+	 * @param specs
+	 * @param refSize
+	 * @param isHor
+	 * @param fillInPushGaps If the gaps are pushing. <b>NOTE!</b> this argument will be filled in and thus changed!
+	 * @return The row gaps in pixel sizes. One more than there are <code>specs</code> sent in.
+	 */
+	private int[][] getRowGaps(DimConstraint[] specs, int refSize, boolean isHor, boolean[] fillInPushGaps)
+	{
+		BoundSize defGap = isHor ? lc.getGridGapX() : lc.getGridGapY();
+		if (defGap == null)
+			defGap = isHor ? PlatformDefaults.getGridGapX() : PlatformDefaults.getGridGapY();
+		int[] defGapArr = defGap.getPixelSizes(refSize, container, null);
+
+		boolean defIns = !hasDocks();
+
+		UnitValue firstGap = LayoutUtil.getInsets(lc, isHor ? 1 : 0, defIns);
+		UnitValue lastGap = LayoutUtil.getInsets(lc, isHor ? 3 : 2, defIns);
+
+        int[][] retValues = new int[specs.length + 1][];
+
+		for (int i = 0, wgIx = 0; i < retValues.length; i++) {
+	        DimConstraint specBefore = i > 0 ? specs[i - 1] : null;
+			DimConstraint specAfter = i < specs.length ? specs[i] : null;
+
+			// No gap if between docking components.
+			boolean edgeBefore = (specBefore == DOCK_DIM_CONSTRAINT || specBefore == null);
+			boolean edgeAfter = (specAfter == DOCK_DIM_CONSTRAINT || specAfter == null);
+			if (edgeBefore && edgeAfter)
+				continue;
+
+			BoundSize wrapGapSize = (wrapGapMap == null || isHor == lc.isFlowX() ? null : wrapGapMap.get(Integer.valueOf(wgIx++)));
+
+			if (wrapGapSize == null) {
+
+				int[] gapBefore = specBefore != null ? specBefore.getRowGaps(container, null, refSize, false) : null;
+				int[] gapAfter = specAfter != null ? specAfter.getRowGaps(container, null, refSize, true) : null;
+
+				if (edgeBefore && gapAfter == null && firstGap != null) {
+
+					int bef = firstGap.getPixels(refSize, container, null);
+					retValues[i] = new int[] {bef, bef, bef};
+
+				} else if (edgeAfter && gapBefore == null && firstGap != null) {
+
+					int aft = lastGap.getPixels(refSize, container, null);
+					retValues[i] = new int[] {aft, aft, aft};
+
+				} else {
+					retValues[i] = gapAfter != gapBefore ? mergeSizes(gapAfter, gapBefore) : new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
+				}
+
+				if (specBefore != null && specBefore.isGapAfterPush() || specAfter != null && specAfter.isGapBeforePush())
+					fillInPushGaps[i] = true;
+			} else {
+
+				if (wrapGapSize.isUnset()) {
+					retValues[i] = new int[] {defGapArr[0], defGapArr[1], defGapArr[2]};
+				} else {
+					retValues[i] = wrapGapSize.getPixelSizes(refSize, container, null);
+				}
+				fillInPushGaps[i] = wrapGapSize.getGapPush();
+			}
+        }
+        return retValues;
+    }
+
+	private static int[][] getGaps(ArrayList<CompWrap> compWraps, boolean isHor)
+	{
+		int compCount = compWraps.size();
+        int[][] retValues = new int[compCount + 1][];
+
+		retValues[0] = compWraps.get(0).getGaps(isHor, true);
+        for (int i = 0; i < compCount; i++) {
+	        int[] gap1 = compWraps.get(i).getGaps(isHor, false);
+	        int[] gap2 = i < compCount - 1 ? compWraps.get(i + 1).getGaps(isHor, true) : null;
+
+			retValues[i + 1] = mergeSizes(gap1, gap2);
+        }
+
+        return retValues;
+    }
+
+	private boolean hasDocks()
+	{
+		return (dockOffX > 0 || dockOffY > 0 || rowIndexes.last() > MAX_GRID || colIndexes.last() > MAX_GRID);
+	}
+
+	/** Adjust min/pref size for columns(or rows) that has components that spans multiple columns (or rows).
+	 * @param specs The specs for the columns or rows. Last index will be used if <code>count</code> is greater than this array's length.
+	 * @param defPush The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
+	 * @param fss
+	 * @param groupsLists
+	 */
+	private void adjustMinPrefForSpanningComps(DimConstraint[] specs, Float[] defPush, FlowSizeSpec fss, ArrayList<LinkedDimGroup>[] groupsLists)
+	{
+		for (int r = groupsLists.length - 1; r >= 0; r--) { // Since 3.7.3 Iterate from end to start. Will solve some multiple spanning components hard to solve problems.
+			ArrayList<LinkedDimGroup> groups = groupsLists[r];
+
+			for (LinkedDimGroup group : groups) {
+				if (group.span == 1)
+					continue;
+
+				int[] sizes = group.getMinPrefMax();
+				for (int s = LayoutUtil.MIN; s <= LayoutUtil.PREF; s++) {
+					int cSize = sizes[s];
+					if (cSize == LayoutUtil.NOT_SET)
+						continue;
+
+					int rowSize = 0;
+					int sIx = (r << 1) + 1;
+					int len = Math.min((group.span << 1), fss.sizes.length - sIx) - 1;
+					for (int j = sIx; j < sIx + len; j++) {
+						int sz = fss.sizes[j][s];
+						if (sz != LayoutUtil.NOT_SET)
+							rowSize += sz;
+					}
+
+					if (rowSize < cSize && len > 0) {
+						for (int eagerness = 0, newRowSize = 0; eagerness < 4 && newRowSize < cSize; eagerness++)
+							newRowSize = fss.expandSizes(specs, defPush, cSize, sIx, len, s, eagerness);
+					}
+				}
+			}
+		}
+	}
+
+	/** For one dimension divide the component wraps into logical groups. One group for component wraps that share a common something,
+	 * line the property to layout by base line.
+	 * @param isRows If rows, and not columns, are to be divided.
+	 * @return One <code>ArrayList<LinkedDimGroup></code> for every row/column.
+	 */
+	private ArrayList<LinkedDimGroup>[] divideIntoLinkedGroups(boolean isRows)
+	{
+		boolean fromEnd = !(isRows ? lc.isTopToBottom() : LayoutUtil.isLeftToRight(lc, container));
+		TreeSet<Integer> primIndexes = isRows ? rowIndexes : colIndexes;
+		TreeSet<Integer> secIndexes = isRows ? colIndexes : rowIndexes;
+		DimConstraint[] primDCs = (isRows ? rowConstr : colConstr).getConstaints();
+
+		@SuppressWarnings("unchecked")
+		ArrayList<LinkedDimGroup>[] groupLists = new ArrayList[primIndexes.size()];
+
+		int gIx = 0;
+		for (int i : primIndexes) {
+
+			DimConstraint dc;
+			if (i >= -MAX_GRID && i <= MAX_GRID) {  // If not dock cell
+				dc = primDCs[i >= primDCs.length ? primDCs.length - 1 : i];
+			} else {
+				dc = DOCK_DIM_CONSTRAINT;
+			}
+
+			ArrayList<LinkedDimGroup> groupList = new ArrayList<LinkedDimGroup>(2);
+			groupLists[gIx++] = groupList;
+
+			for (Integer ix : secIndexes) {
+				Cell cell = isRows ? getCell(i, ix) : getCell(ix, i);
+				if (cell == null || cell.compWraps.size() == 0)
+					continue;
+
+				int span = (isRows ? cell.spany : cell.spanx);
+				if (span > 1)
+					span = convertSpanToSparseGrid(i, span, primIndexes);
+
+				boolean isPar = (cell.flowx == isRows);
+
+				if ((isPar == false && cell.compWraps.size() > 1) || span > 1) {
+
+					int linkType = isPar ? LinkedDimGroup.TYPE_PARALLEL : LinkedDimGroup.TYPE_SERIAL;
+					LinkedDimGroup lg = new LinkedDimGroup("p," + ix, span, linkType, !isRows, fromEnd);
+					lg.setCompWraps(cell.compWraps);
+					groupList.add(lg);
+				} else {
+					for (int cwIx = 0; cwIx < cell.compWraps.size(); cwIx++) {
+						CompWrap cw = cell.compWraps.get(cwIx);
+						boolean rowBaselineAlign = (isRows && lc.isTopToBottom() && dc.getAlignOrDefault(!isRows) == UnitValue.BASELINE_IDENTITY); // Disable baseline for bottomToTop since I can not verify it working.
+						boolean isBaseline = isRows && cw.isBaselineAlign(rowBaselineAlign);
+
+						String linkCtx = isBaseline ? "baseline" : null;
+
+						// Find a group with same link context and put it in that group.
+						boolean foundList = false;
+						for (int glIx = 0, lastGl = groupList.size() - 1; glIx <= lastGl; glIx++) {
+							LinkedDimGroup group = groupList.get(glIx);
+							if (group.linkCtx == linkCtx || linkCtx != null && linkCtx.equals(group.linkCtx)) {
+								group.addCompWrap(cw);
+								foundList = true;
+								break;
+							}
+						}
+
+						// If none found and at last add a new group.
+						if (foundList == false) {
+							int linkType = isBaseline ? LinkedDimGroup.TYPE_BASELINE : LinkedDimGroup.TYPE_PARALLEL;
+							LinkedDimGroup lg = new LinkedDimGroup(linkCtx, 1, linkType, !isRows, fromEnd);
+							lg.addCompWrap(cw);
+							groupList.add(lg);
+						}
+					}
+				}
+			}
+		}
+		return groupLists;
+	}
+
+	/** Spanning is specified in the uncompressed grid number. They can for instance be more than 60000 for the outer
+	 * edge dock grid cells. When the grid is compressed and indexed after only the cells that area occupied the span
+	 * is erratic. This method use the row/col indexes and corrects the span to be correct for the compressed grid.
+	 * @param span The span un the uncompressed grid. <code>LayoutUtil.INF</code> will be interpreted to span the rest
+	 * of the column/row excluding the surrounding docking components.
+	 * @param indexes The indexes in the correct dimension.
+	 * @return The converted span.
+	 */
+	private static int convertSpanToSparseGrid(int curIx, int span, TreeSet<Integer> indexes)
+	{
+		int lastIx = curIx + span;
+		int retSpan = 1;
+
+		for (Integer ix : indexes) {
+			if (ix <= curIx)
+				continue;   // We have not arrived to the correct index yet
+
+			if (ix >= lastIx)
+				break;
+
+			retSpan++;
+		}
+		return retSpan;
+	}
+
+	private boolean isCellFree(int r, int c, ArrayList<int[]> occupiedRects)
+	{
+		if (getCell(r, c) != null)
+			return false;
+
+		for (int[] rect : occupiedRects) {
+			if (rect[0] <= c && rect[1] <= r && rect[0] + rect[2] > c && rect[1] + rect[3] > r)
+				return false;
+		}
+		return true;
+	}
+
+	private Cell getCell(int r, int c)
+	{
+		return grid.get(Integer.valueOf((r << 16) + c));
+	}
+
+	private void setCell(int r, int c, Cell cell)
+	{
+		if (c < 0 || r < 0)
+			throw new IllegalArgumentException("Cell position cannot be negative. row: " + r + ", col: " + c);
+
+		if (c > MAX_GRID || r > MAX_GRID)
+			throw new IllegalArgumentException("Cell position out of bounds. Out of cells. row: " + r + ", col: " + c);
+
+		rowIndexes.add(r);
+		colIndexes.add(c);
+
+		grid.put((r << 16) + c, cell);
+	}
+
+	/** Adds a docking cell. That cell is outside the normal cell indexes.
+	 * @param dockInsets The current dock insets. Will be updated!
+	 * @param side top == 0, left == 1, bottom = 2, right = 3.
+	 * @param cw The compwrap to put in a cell and add.
+	 */
+	private void addDockingCell(int[] dockInsets, int side, CompWrap cw)
+	{
+		int r, c, spanx = 1, spany = 1;
+		switch (side) {
+			case 0:
+			case 2:
+				r = side == 0 ? dockInsets[0]++ : dockInsets[2]--;
+				c = dockInsets[1];
+				spanx = dockInsets[3] - dockInsets[1] + 1;  // The +1 is for cell 0.
+				colIndexes.add(dockInsets[3]); // Make sure there is a receiving cell
+				break;
+
+			case 1:
+			case 3:
+				c = side == 1 ? dockInsets[1]++ : dockInsets[3]--;
+				r = dockInsets[0];
+				spany = dockInsets[2] - dockInsets[0] + 1;  // The +1 is for cell 0.
+				rowIndexes.add(dockInsets[2]); // Make sure there is a receiving cell
+				break;
+
+			default:
+				throw new IllegalArgumentException("Internal error 123.");
+		}
+
+		rowIndexes.add(r);
+		colIndexes.add(c);
+
+		grid.put((r << 16) + c, new Cell(cw, spanx, spany, spanx > 1));
+	}
+
+	/** A simple representation of a cell in the grid. Contains a number of component wraps and if they span more than one cell.
+	 */
+	private static class Cell
+	{
+		private final int spanx, spany;
+		private final boolean flowx;
+		private final ArrayList<CompWrap> compWraps = new ArrayList<CompWrap>(1);
+
+		private boolean hasTagged = false;  // If one or more components have styles and need to be checked by the component sorter
+
+		private Cell(CompWrap cw)
+		{
+			this(cw, 1, 1, true);
+		}
+
+		private Cell(int spanx, int spany, boolean flowx)
+		{
+			this(null, spanx, spany, flowx);
+		}
+
+		private Cell(CompWrap cw, int spanx, int spany, boolean flowx)
+		{
+			if (cw != null)
+				compWraps.add(cw);
+			this.spanx = spanx;
+			this.spany = spany;
+			this.flowx = flowx;
+		}
+	}
+
+	/** A number of component wraps that share a layout "something" <b>in one dimension</b>
+	 */
+	private static class LinkedDimGroup
+	{
+		private static final int TYPE_SERIAL = 0;
+		private static final int TYPE_PARALLEL = 1;
+		private static final int TYPE_BASELINE = 2;
+
+		private final String linkCtx;
+		private final int span;
+		private final int linkType;
+		private final boolean isHor, fromEnd;
+
+		private ArrayList<CompWrap> _compWraps = new ArrayList<CompWrap>(4);
+
+		private int[] sizes = null;
+		private int lStart = 0, lSize = 0;  // Currently mostly for debug painting
+
+		private LinkedDimGroup(String linkCtx, int span, int linkType, boolean isHor, boolean fromEnd)
+		{
+			this.linkCtx = linkCtx;
+			this.span = span;
+			this.linkType = linkType;
+			this.isHor = isHor;
+			this.fromEnd = fromEnd;
+		}
+
+		private void addCompWrap(CompWrap cw)
+		{
+			_compWraps.add(cw);
+			sizes = null;
+		}
+
+		private void setCompWraps(ArrayList<CompWrap> cws)
+		{
+			if (_compWraps != cws) {
+				_compWraps = cws;
+				sizes = null;
+			}
+		}
+
+		private void layout(DimConstraint dc, int start, int size, int spanCount)
+		{
+			lStart = start;
+			lSize = size;
+
+			if (_compWraps.size() == 0)
+				return;
+
+			ContainerWrapper parent = _compWraps.get(0).comp.getParent();
+			if (linkType == TYPE_PARALLEL) {
+				layoutParallel(parent, _compWraps, dc, start, size, isHor, fromEnd);
+			} else if (linkType == TYPE_BASELINE) {
+				layoutBaseline(parent, _compWraps, dc, start, size, LayoutUtil.PREF, spanCount);
+			} else {
+				layoutSerial(parent, _compWraps, dc, start, size, isHor, spanCount, fromEnd);
+			}
+		}
+
+		/** Returns the min/pref/max sizes for this cell. Returned array <b>must not be altered</b>
+		 * @return A shared min/pref/max array of sizes. Always of length 3 and never <code>null</code>. Will always be of type STATIC and PIXEL.
+		 */
+		private int[] getMinPrefMax()
+		{
+			if (sizes == null && _compWraps.size() > 0) {
+				sizes = new int[3];
+				for (int sType = LayoutUtil.MIN; sType <= LayoutUtil.PREF; sType++) {
+					if (linkType == TYPE_PARALLEL) {
+						sizes[sType] = getTotalSizeParallel(_compWraps, sType, isHor);
+					} else if (linkType == TYPE_BASELINE) {
+						int[] aboveBelow = getBaselineAboveBelow(_compWraps, sType, false);
+						sizes[sType] = aboveBelow[0] + aboveBelow[1];
+					} else {
+						sizes[sType] = getTotalSizeSerial(_compWraps, sType, isHor);
+					}
+				}
+				sizes[LayoutUtil.MAX] = LayoutUtil.INF;
+			}
+			return sizes;
+		}
+	}
+
+	/** Wraps a {@link java.awt.Component} together with its constraint. Caches a lot of information about the component so
+	 * for instance not the preferred size has to be calculated more than once.
+	 */
+	private final static class CompWrap
+	{
+		private final ComponentWrapper comp;
+		private final CC cc;
+		private final UnitValue[] pos;
+		private int[][] gaps; // [top,left(actually before),bottom,right(actually after)][min,pref,max]
+
+		private final int[] horSizes = new int[3];
+		private final int[] verSizes = new int[3];
+
+		private int x = LayoutUtil.NOT_SET, y = LayoutUtil.NOT_SET, w = LayoutUtil.NOT_SET, h = LayoutUtil.NOT_SET;
+
+		private int forcedPushGaps = 0;   // 1 == before, 2 = after. Bitwise.
+
+		private CompWrap(ComponentWrapper c, CC cc, int eHideMode, UnitValue[] pos, BoundSize[] callbackSz)
+		{
+			this.comp = c;
+			this.cc = cc;
+			this.pos = pos;
+
+			if (eHideMode <= 0) {
+				BoundSize hBS = (callbackSz != null && callbackSz[0] != null) ? callbackSz[0] : cc.getHorizontal().getSize();
+				BoundSize vBS = (callbackSz != null && callbackSz[1] != null) ? callbackSz[1] : cc.getVertical().getSize();
+
+				int wHint = -1, hHint = -1; // Added for v3.7
+				if (comp.getWidth() > 0 && comp.getHeight() > 0) {
+					hHint = comp.getHeight();
+					wHint = comp.getWidth();
+				}
+
+				for (int i = LayoutUtil.MIN; i <= LayoutUtil.MAX; i++) {
+					horSizes[i] = getSize(hBS, i, true, hHint);
+					verSizes[i] = getSize(vBS, i, false, wHint > 0 ? wHint : horSizes[i]);
+				}
+
+				correctMinMax(horSizes);
+				correctMinMax(verSizes);
+			}
+
+			if (eHideMode > 1) {
+				gaps = new int[4][];
+				for (int i = 0; i < gaps.length; i++)
+					gaps[i] = new int[3];
+			}
+		}
+
+		private int getSize(BoundSize uvs, int sizeType, boolean isHor, int sizeHint)
+		{
+			if (uvs == null || uvs.getSize(sizeType) == null) {
+				switch(sizeType) {
+					case LayoutUtil.MIN:
+						return isHor ? comp.getMinimumWidth(sizeHint) : comp.getMinimumHeight(sizeHint);
+					case LayoutUtil.PREF:
+						return isHor ? comp.getPreferredWidth(sizeHint) : comp.getPreferredHeight(sizeHint);
+					default:
+						return isHor ? comp.getMaximumWidth(sizeHint) : comp.getMaximumHeight(sizeHint);
+				}
+			}
+
+			ContainerWrapper par = comp.getParent();
+			return uvs.getSize(sizeType).getPixels(isHor ? par.getWidth() : par.getHeight(), par, comp);
+		}
+
+		private void calcGaps(ComponentWrapper before, CC befCC, ComponentWrapper after, CC aftCC, String tag, boolean flowX, boolean isLTR)
+		{
+			ContainerWrapper par = comp.getParent();
+			int parW = par.getWidth();
+			int parH = par.getHeight();
+
+			BoundSize befGap = before != null ? (flowX ? befCC.getHorizontal() : befCC.getVertical()).getGapAfter() : null;
+			BoundSize aftGap = after != null ? (flowX ? aftCC.getHorizontal() : aftCC.getVertical()).getGapBefore() : null;
+
+			mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, befGap, (flowX ? null : before), tag, parH, 0, isLTR), false, true);
+			mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, befGap, (flowX ? before : null), tag, parW, 1, isLTR), true, true);
+			mergeGapSizes(cc.getVertical().getComponentGaps(par, comp, aftGap, (flowX ? null : after), tag, parH, 2, isLTR), false, false);
+			mergeGapSizes(cc.getHorizontal().getComponentGaps(par, comp, aftGap, (flowX ? after : null), tag, parW, 3, isLTR), true, false);
+		}
+
+		private void setDimBounds(int start, int size, boolean isHor)
+		{
+			if (isHor) {
+				x = start;
+				w = size;
+			} else {
+				y = start;
+				h = size;
+			}
+		}
+
+		private boolean isPushGap(boolean isHor, boolean isBefore)
+		{
+			if (isHor && ((isBefore ? 1 : 2) & forcedPushGaps) != 0)
+				return true;    // Forced
+
+			DimConstraint dc = cc.getDimConstraint(isHor);
+			BoundSize s = isBefore ? dc.getGapBefore() : dc.getGapAfter();
+			return s != null && s.getGapPush();
+		}
+
+		/**
+		 * @return If the preferred size have changed because of the new bounds.
+		 */
+		private boolean transferBounds(boolean checkPrefChange)
+		{
+			comp.setBounds(x, y, w, h);
+
+			if (checkPrefChange && w != horSizes[LayoutUtil.PREF]) {
+				BoundSize vSz = cc.getVertical().getSize();
+				if (vSz.getPreferred() == null) {
+					if (comp.getPreferredHeight(-1) != verSizes[LayoutUtil.PREF])
+						return true;
+				}
+			}
+			return false;
+		}
+
+		private void setSizes(int[] sizes, boolean isHor)
+		{
+			if (sizes == null)
+				return;
+
+			int[] s = isHor ? horSizes : verSizes;
+            s[LayoutUtil.MIN] = sizes[LayoutUtil.MIN];
+            s[LayoutUtil.PREF] = sizes[LayoutUtil.PREF];
+            s[LayoutUtil.MAX] = sizes[LayoutUtil.MAX];
+		}
+
+		private void setGaps(int[] minPrefMax, int ix)
+		{
+			if (gaps == null)
+				gaps = new int[][] {null, null, null, null};
+
+			gaps[ix] = minPrefMax;
+		}
+
+		private void mergeGapSizes(int[] sizes, boolean isHor, boolean isTL)
+		{
+			if (gaps == null)
+				gaps = new int[][] {null, null, null, null};
+
+			if (sizes == null)
+				return;
+
+			int gapIX = getGapIx(isHor, isTL);
+			int[] oldGaps = gaps[gapIX];
+			if (oldGaps == null) {
+				oldGaps = new int[] {0, 0, LayoutUtil.INF};
+				gaps[gapIX] = oldGaps;
+			}
+
+			oldGaps[LayoutUtil.MIN] = Math.max(sizes[LayoutUtil.MIN], oldGaps[LayoutUtil.MIN]);
+			oldGaps[LayoutUtil.PREF] = Math.max(sizes[LayoutUtil.PREF], oldGaps[LayoutUtil.PREF]);
+			oldGaps[LayoutUtil.MAX] = Math.min(sizes[LayoutUtil.MAX], oldGaps[LayoutUtil.MAX]);
+		}
+
+		private int getGapIx(boolean isHor, boolean isTL)
+		{
+			return isHor ? (isTL ? 1 : 3) : (isTL ? 0 : 2);
+		}
+
+		private int getSizeInclGaps(int sizeType, boolean isHor)
+		{
+			return filter(sizeType, getGapBefore(sizeType, isHor) + getSize(sizeType, isHor) + getGapAfter(sizeType, isHor));
+		}
+
+		private int getSize(int sizeType, boolean isHor)
+		{
+			return filter(sizeType, isHor ? horSizes[sizeType] : verSizes[sizeType]);
+		}
+
+		private int getGapBefore(int sizeType, boolean isHor)
+		{
+			int[] gaps = getGaps(isHor, true);
+			return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
+		}
+
+		private int getGapAfter(int sizeType, boolean isHor)
+		{
+			int[] gaps = getGaps(isHor, false);
+			return gaps != null ? filter(sizeType, gaps[sizeType]) : 0;
+		}
+
+		private int[] getGaps(boolean isHor, boolean isTL)
+		{
+			return gaps[getGapIx(isHor, isTL)];
+		}
+
+		private int filter(int sizeType, int size)
+		{
+			if (size == LayoutUtil.NOT_SET)
+				return sizeType != LayoutUtil.MAX ? 0 : LayoutUtil.INF;
+			return constrainSize(size);
+		}
+
+		private boolean isBaselineAlign(boolean defValue)
+		{
+			Float g = cc.getVertical().getGrow();
+			if (g != null && g.intValue() != 0)
+				return false;
+
+			UnitValue al = cc.getVertical().getAlign();
+			return (al != null ? al == UnitValue.BASELINE_IDENTITY : defValue) && comp.hasBaseline();
+		}
+
+		private int getBaseline(int sizeType)
+		{
+			return comp.getBaseline(getSize(sizeType, true), getSize(sizeType, false));
+		}
+	}
+
+	//***************************************************************************************
+	//* Helper Methods
+	//***************************************************************************************
+
+	private static void layoutBaseline(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, int sizeType, int spanCount)
+	{
+		int[] aboveBelow = getBaselineAboveBelow(compWraps, sizeType, true);
+		int blRowSize = aboveBelow[0] + aboveBelow[1];
+
+		CC cc = compWraps.get(0).cc;
+
+		// Align for the whole baseline component array
+		UnitValue align = cc.getVertical().getAlign();
+		if (spanCount == 1 && align == null)
+			align = dc.getAlignOrDefault(false);
+		if (align == UnitValue.BASELINE_IDENTITY)
+			align = UnitValue.CENTER;
+
+		int offset = start + aboveBelow[0] + (align != null ? Math.max(0, align.getPixels(size - blRowSize, parent, null)) : 0);
+		for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+			CompWrap cw = compWraps.get(i);
+			cw.y += offset;
+			if (cw.y + cw.h > start + size)
+				cw.h = start + size - cw.y;
+		}
+	}
+
+	private static void layoutSerial(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, int spanCount, boolean fromEnd)
+	{
+		FlowSizeSpec fss = mergeSizesGapsAndResConstrs(
+				getComponentResizeConstraints(compWraps, isHor),
+		        getComponentGapPush(compWraps, isHor),
+				getComponentSizes(compWraps, isHor),
+				getGaps(compWraps, isHor));
+
+		Float[] pushW = dc.isFill() ? GROW_100 : null;
+		int[] sizes = LayoutUtil.calculateSerial(fss.sizes, fss.resConstsInclGaps, pushW, LayoutUtil.PREF, size);
+		setCompWrapBounds(parent, sizes, compWraps, dc.getAlignOrDefault(isHor),  start, size, isHor, fromEnd);
+	}
+
+	private static void setCompWrapBounds(ContainerWrapper parent, int[] allSizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign,  int start, int size, boolean isHor, boolean fromEnd)
+	{
+		int totSize = LayoutUtil.sum(allSizes);
+		CC cc = compWraps.get(0).cc;
+		UnitValue align = correctAlign(cc, rowAlign, isHor, fromEnd);
+
+		int cSt = start;
+		int slack = size - totSize;
+		if (slack > 0 && align != null) {
+			int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
+			cSt += (fromEnd ? -al : al);
+		}
+
+		for (int i = 0, bIx = 0, iSz = compWraps.size(); i < iSz; i++) {
+			CompWrap cw = compWraps.get(i);
+			if (fromEnd ) {
+				cSt -= allSizes[bIx++];
+				cw.setDimBounds(cSt - allSizes[bIx], allSizes[bIx], isHor);
+				cSt -= allSizes[bIx++];
+			} else {
+				cSt += allSizes[bIx++];
+				cw.setDimBounds(cSt, allSizes[bIx], isHor);
+				cSt += allSizes[bIx++];
+			}
+		}
+	}
+
+	private static void layoutParallel(ContainerWrapper parent, ArrayList<CompWrap> compWraps, DimConstraint dc, int start, int size, boolean isHor, boolean fromEnd)
+	{
+		int[][] sizes = new int[compWraps.size()][];    // [compIx][gapBef,compSize,gapAft]
+
+		for (int i = 0; i < sizes.length; i++) {
+			CompWrap cw = compWraps.get(i);
+
+			DimConstraint cDc = cw.cc.getDimConstraint(isHor);
+
+			ResizeConstraint[] resConstr = new ResizeConstraint[] {
+					cw.isPushGap(isHor, true) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
+					cDc.resize,
+					cw.isPushGap(isHor, false) ? GAP_RC_CONST_PUSH : GAP_RC_CONST,
+			};
+
+			int[][] sz = new int[][] {
+				cw.getGaps(isHor, true), (isHor ? cw.horSizes : cw.verSizes), cw.getGaps(isHor, false)
+			};
+
+			Float[] pushW = dc.isFill() ? GROW_100 : null;
+
+			sizes[i] = LayoutUtil.calculateSerial(sz, resConstr, pushW, LayoutUtil.PREF, size);
+		}
+
+		UnitValue rowAlign = dc.getAlignOrDefault(isHor);
+		setCompWrapBounds(parent, sizes, compWraps, rowAlign, start, size, isHor, fromEnd);
+	}
+
+	private static void setCompWrapBounds(ContainerWrapper parent, int[][] sizes, ArrayList<CompWrap> compWraps, UnitValue rowAlign,  int start, int size, boolean isHor, boolean fromEnd)
+	{
+		for (int i = 0; i < sizes.length; i++) {
+			CompWrap cw = compWraps.get(i);
+
+			UnitValue align = correctAlign(cw.cc, rowAlign, isHor, fromEnd);
+
+			int[] cSizes = sizes[i];
+			int gapBef = cSizes[0];
+			int cSize = cSizes[1];  // No Math.min(size, cSizes[1]) here!
+			int gapAft = cSizes[2];
+
+			int cSt = fromEnd ? start - gapBef : start + gapBef;
+			int slack = size - cSize - gapBef - gapAft;
+			if (slack > 0 && align != null) {
+				int al = Math.min(slack, Math.max(0, align.getPixels(slack, parent, null)));
+				cSt += (fromEnd ? -al : al);
+			}
+
+			cw.setDimBounds(fromEnd ? cSt - cSize : cSt, cSize, isHor);
+		}
+	}
+
+	private static UnitValue correctAlign(CC cc, UnitValue rowAlign, boolean isHor, boolean fromEnd)
+	{
+		UnitValue align = (isHor ? cc.getHorizontal() : cc.getVertical()).getAlign();
+		if (align == null)
+			align = rowAlign;
+		if (align == UnitValue.BASELINE_IDENTITY)
+			align = UnitValue.CENTER;
+
+		if (fromEnd) {
+			if (align == UnitValue.LEFT)
+				align = UnitValue.RIGHT;
+			else if (align == UnitValue.RIGHT)
+				align = UnitValue.LEFT;
+		}
+		return align;
+	}
+
+	private static int[] getBaselineAboveBelow(ArrayList<CompWrap> compWraps, int sType, boolean centerBaseline)
+	{
+		int maxAbove = Short.MIN_VALUE;
+		int maxBelow = Short.MIN_VALUE;
+		for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+			CompWrap cw = compWraps.get(i);
+
+			int height = cw.getSize(sType, false);
+			if (height >= LayoutUtil.INF)
+				return new int[] {LayoutUtil.INF / 2, LayoutUtil.INF / 2};
+
+			int baseline = cw.getBaseline(sType);
+			int above = baseline + cw.getGapBefore(sType, false);
+			maxAbove = Math.max(above, maxAbove);
+			maxBelow = Math.max(height - baseline + cw.getGapAfter(sType, false), maxBelow);
+
+			if (centerBaseline)
+				cw.setDimBounds(-baseline, height, false);
+		}
+		return new int[] {maxAbove, maxBelow};
+	}
+
+	private static int getTotalSizeParallel(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
+	{
+		int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
+
+		for (int i = 0, iSz = compWraps.size(); i < iSz; i++) {
+			CompWrap cw = compWraps.get(i);
+			int cwSize = cw.getSizeInclGaps(sType, isHor);
+			if (cwSize >= LayoutUtil.INF)
+				return LayoutUtil.INF;
+
+			if (sType == LayoutUtil.MAX ? cwSize < size : cwSize > size)
+		        size = cwSize;
+		}
+		return constrainSize(size);
+	}
+
+	private static int getTotalSizeSerial(ArrayList<CompWrap> compWraps, int sType, boolean isHor)
+	{
+		int totSize = 0;
+		for (int i = 0, iSz = compWraps.size(), lastGapAfter = 0; i < iSz; i++) {
+			CompWrap wrap = compWraps.get(i);
+			int gapBef = wrap.getGapBefore(sType, isHor);
+			if (gapBef > lastGapAfter)
+				totSize += gapBef - lastGapAfter;
+
+			totSize += wrap.getSize(sType, isHor);
+			totSize += (lastGapAfter = wrap.getGapAfter(sType, isHor));
+
+			if (totSize >= LayoutUtil.INF)
+				return LayoutUtil.INF;
+		}
+		return constrainSize(totSize);
+	}
+
+	private static int getTotalGroupsSizeParallel(ArrayList<LinkedDimGroup> groups, int sType, boolean countSpanning)
+	{
+		int size = sType == LayoutUtil.MAX ? LayoutUtil.INF : 0;
+		for (int i = 0, iSz = groups.size(); i < iSz; i++) {
+			LinkedDimGroup group = groups.get(i);
+			if (countSpanning || group.span == 1) {
+				int grpSize = group.getMinPrefMax()[sType];
+				if (grpSize >= LayoutUtil.INF)
+					return LayoutUtil.INF;
+
+				if (sType == LayoutUtil.MAX ? grpSize < size : grpSize > size)
+			        size = grpSize;
+			}
+		}
+		return constrainSize(size);
+	}
+
+	/**
+	 * @param compWraps
+	 * @param isHor
+	 * @return Might contain LayoutUtil.NOT_SET
+	 */
+	private static int[][] getComponentSizes(ArrayList<CompWrap> compWraps, boolean isHor)
+	{
+		int[][] compSizes = new int[compWraps.size()][];
+		for (int i = 0; i < compSizes.length; i++) {
+			CompWrap cw = compWraps.get(i);
+			compSizes[i] = isHor ? cw.horSizes : cw.verSizes;
+		}
+		return compSizes;
+	}
+
+	/** Merges sizes and gaps together with Resize Constraints. For gaps {@link #GAP_RC_CONST} is used.
+	 * @param resConstr One resize constriant for every row/component. Can be lesser in length and the last element should be used for missing elements.
+	 * @param gapPush If the corresponding gap should be considered pushing and thus want to take free space if left over. Should be one more than resConstrs!
+	 * @param minPrefMaxSizes The sizes (min/pref/max) for every row/component.
+	 * @param gapSizes The gaps before and after each row/component packed in one double sized array.
+	 * @return A holder for the merged values.
+	 */
+	private static FlowSizeSpec mergeSizesGapsAndResConstrs(ResizeConstraint[] resConstr, boolean[] gapPush, int[][] minPrefMaxSizes, int[][] gapSizes)
+	{
+		int[][] sizes = new int[(minPrefMaxSizes.length << 1) + 1][];  // Make room for gaps around.
+		ResizeConstraint[] resConstsInclGaps = new ResizeConstraint[sizes.length];
+
+		sizes[0] = gapSizes[0];
+		for (int i = 0, crIx = 1; i < minPrefMaxSizes.length; i++, crIx += 2) {
+
+			// Component bounds and constraints
+			resConstsInclGaps[crIx] = resConstr[i];
+			sizes[crIx] = minPrefMaxSizes[i];
+
+			sizes[crIx + 1] = gapSizes[i + 1];
+
+			if (sizes[crIx - 1] != null)
+				resConstsInclGaps[crIx - 1] = gapPush[i < gapPush.length ? i : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
+
+			if (i == (minPrefMaxSizes.length - 1) && sizes[crIx + 1] != null)
+				resConstsInclGaps[crIx + 1] = gapPush[(i + 1) < gapPush.length ? (i + 1) : gapPush.length - 1] ? GAP_RC_CONST_PUSH : GAP_RC_CONST;
+		}
+
+		// Check for null and set it to 0, 0, 0.
+		for (int i = 0; i < sizes.length; i++) {
+			if (sizes[i] == null)
+				sizes[i] = new int[3];
+		}
+
+		return new FlowSizeSpec(sizes, resConstsInclGaps);
+	}
+
+	private static int[] mergeSizes(int[] oldValues, int[] newValues)
+	{
+		if (oldValues == null)
+			return newValues;
+
+		if (newValues == null)
+			return oldValues;
+
+		int[] ret = new int[oldValues.length];
+		for (int i = 0; i < ret.length; i++)
+			ret[i] = mergeSizes(oldValues[i], newValues[i], true);
+
+		return ret;
+	}
+
+	private static int mergeSizes(int oldValue, int newValue, boolean toMax)
+	{
+		if (oldValue == LayoutUtil.NOT_SET || oldValue == newValue)
+			return newValue;
+
+		if (newValue == LayoutUtil.NOT_SET)
+			return oldValue;
+
+		return toMax != oldValue > newValue ? newValue : oldValue;
+	}
+
+	private static int constrainSize(int s)
+	{
+		return s > 0 ? (s < LayoutUtil.INF ? s : LayoutUtil.INF) : 0;
+	}
+
+	private static void correctMinMax(int s[])
+	{
+		if (s[LayoutUtil.MIN] > s[LayoutUtil.MAX])
+			s[LayoutUtil.MIN] = s[LayoutUtil.MAX];  // Since MAX is almost always explicitly set use that
+
+		if (s[LayoutUtil.PREF] < s[LayoutUtil.MIN])
+			s[LayoutUtil.PREF] = s[LayoutUtil.MIN];
+
+		if (s[LayoutUtil.PREF] > s[LayoutUtil.MAX])
+			s[LayoutUtil.PREF] = s[LayoutUtil.MAX];
+	}
+
+	private static final class FlowSizeSpec
+	{
+		private final int[][] sizes;  // [row/col index][min, pref, max]
+		private final ResizeConstraint[] resConstsInclGaps;  // [row/col index]
+
+		private FlowSizeSpec(int[][] sizes, ResizeConstraint[] resConstsInclGaps)
+		{
+			this.sizes = sizes;
+			this.resConstsInclGaps = resConstsInclGaps;
+		}
+
+		/**
+		 * @param specs The specs for the columns or rows. Last index will be used of <code>fromIx + len</code> is greater than this array's length.
+		 * @param targetSize The size to try to meet.
+		 * @param defGrow The default grow weight if the specs does not have anyone that will grow. Comes from "push" in the CC.
+		 * @param fromIx
+		 * @param len
+		 * @param sizeType
+		 * @param eagerness How eager the algorithm should be to try to expand the sizes.
+		 * <ul>
+		 * <li>0 - Grow only rows/columns which have the <code>sizeType</code> set to be the containing components AND which has a grow weight > 0.
+		 * <li>1 - Grow only rows/columns which have the <code>sizeType</code> set to be the containing components AND which has a grow weight > 0 OR unspecified.
+		 * <li>2 - Grow all rows/columns that have a grow weight > 0.
+		 * <li>3 - Grow all rows/columns that have a grow weight > 0 OR unspecified.
+		 * </ul>
+		 * @return The new size.
+		 */
+		private int expandSizes(DimConstraint[] specs, Float[] defGrow, int targetSize, int fromIx, int len, int sizeType, int eagerness)
+		{
+			ResizeConstraint[] resConstr = new ResizeConstraint[len];
+			int[][] sizesToExpand = new int[len][];
+			for (int i = 0; i < len; i++) {
+				int[] minPrefMax = sizes[i + fromIx];
+				sizesToExpand[i] = new int[] {minPrefMax[sizeType], minPrefMax[LayoutUtil.PREF], minPrefMax[LayoutUtil.MAX]};
+
+				if (eagerness <= 1 && i % 2 == 0) { // (i % 2 == 0) means only odd indexes, which is only rows/col indexes and not gaps.
+					int cIx = (i + fromIx - 1) >> 1;
+					DimConstraint spec = (DimConstraint) LayoutUtil.getIndexSafe(specs, cIx);
+
+					BoundSize sz = spec.getSize();
+					if (    (sizeType == LayoutUtil.MIN && sz.getMin() != null && sz.getMin().getUnit() != UnitValue.MIN_SIZE) ||
+					        (sizeType == LayoutUtil.PREF && sz.getPreferred() != null && sz.getPreferred().getUnit() != UnitValue.PREF_SIZE)) {
+						continue;
+					}
+				}
+				resConstr[i] = (ResizeConstraint) LayoutUtil.getIndexSafe(resConstsInclGaps, i + fromIx);
+			}
+
+			Float[] growW = (eagerness == 1 || eagerness == 3) ? extractSubArray(specs, defGrow, fromIx, len): null;
+			int[] newSizes = LayoutUtil.calculateSerial(sizesToExpand, resConstr, growW, LayoutUtil.PREF, targetSize);
+			int newSize = 0;
+
+			for (int i = 0; i < len; i++) {
+				int s = newSizes[i];
+				sizes[i + fromIx][sizeType] = s;
+				newSize += s;
+			}
+			return newSize;
+		}
+	}
+
+	private static Float[] extractSubArray(DimConstraint[] specs, Float[] arr, int ix, int len)
+	{
+		if (arr == null || arr.length < ix + len) {
+			Float[] growLastArr = new Float[len];
+
+			// Handle a group where some rows (first one/few and/or last one/few) are docks.
+			for (int i = ix + len - 1; i >= 0; i -= 2) {
+				int specIx = (i >> 1);
+				if (specs[specIx] != DOCK_DIM_CONSTRAINT) {
+					growLastArr[i - ix] = ResizeConstraint.WEIGHT_100;
+					return growLastArr;
+				}
+			}
+			return growLastArr;
+		}
+
+		Float[] newArr = new Float[len];
+		for (int i = 0; i < len; i++)
+			newArr[i] = arr[ix + i];
+		return newArr;
+	}
+
+	@SuppressWarnings("rawtypes")
+	private static WeakHashMap[] PARENT_ROWCOL_SIZES_MAP = null;
+	@SuppressWarnings("unchecked")
+	private static synchronized void putSizesAndIndexes(Object parComp, int[] sizes, int[] ixArr, boolean isRows)
+	{
+		if (PARENT_ROWCOL_SIZES_MAP == null)    // Lazy since only if designing in IDEs
+			PARENT_ROWCOL_SIZES_MAP = new WeakHashMap[] {new WeakHashMap<Object, Object>(4), new WeakHashMap<Object, Object>(4)};
+
+		PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].put(parComp, new int[][] {ixArr, sizes});
+	}
+
+	static synchronized int[][] getSizesAndIndexes(Object parComp, boolean isRows)
+	{
+		if (PARENT_ROWCOL_SIZES_MAP == null)
+			return null;
+
+		return (int[][]) PARENT_ROWCOL_SIZES_MAP[isRows ? 0 : 1].get(parComp);
+	}
+
+	private static WeakHashMap<Object, LinkedHashMap<Integer, Cell>> PARENT_GRIDPOS_MAP = null;
+	private static synchronized void saveGrid(ComponentWrapper parComp, LinkedHashMap<Integer, Cell> grid)
+	{
+		if (PARENT_GRIDPOS_MAP == null)    // Lazy since only if designing in IDEs
+			PARENT_GRIDPOS_MAP = new WeakHashMap<Object, LinkedHashMap<Integer, Cell>>();
+
+		PARENT_GRIDPOS_MAP.put(parComp.getComponent(), grid);
+	}
+
+	static synchronized HashMap<Object, int[]> getGridPositions(Object parComp)
+	{
+		if (PARENT_GRIDPOS_MAP == null)
+			return null;
+
+		LinkedHashMap<Integer, Cell> grid = PARENT_GRIDPOS_MAP.get(parComp);
+		if (grid == null)
+			return null;
+
+		HashMap<Object, int[]> retMap = new HashMap<Object,int[]>();
+
+		for (Map.Entry<Integer, Cell> e : grid.entrySet()) {
+			Cell cell = e.getValue();
+			Integer xyInt = e.getKey();
+			if (xyInt != null) {
+				int x = xyInt & 0x0000ffff;
+				int y = xyInt >> 16;
+
+				for (CompWrap cw : cell.compWraps)
+					retMap.put(cw.comp.getComponent(), new int[]{x, y, cell.spanx, cell.spany});
+			}
+		}
+
+		return retMap;
+	}
+}
diff --git a/src/net/miginfocom/layout/IDEUtil.java b/src/net/miginfocom/layout/IDEUtil.java
new file mode 100644
index 0000000..fb83e12
--- /dev/null
+++ b/src/net/miginfocom/layout/IDEUtil.java
@@ -0,0 +1,789 @@
+package net.miginfocom.layout;
+
+import java.util.HashMap;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** This class contains static methods to be used by IDE vendors to convert to and from String/API constraints.
+ * <p>
+ * <b>Note that {@link LayoutUtil#setDesignTime(ContainerWrapper, boolean)} should be set to <code>true</code> for this class'
+ * methods to work.</b>
+ */
+public class IDEUtil
+{
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue ZERO = UnitValue.ZERO;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue TOP = UnitValue.TOP;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue LEADING = UnitValue.LEADING;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue LEFT = UnitValue.LEFT;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue CENTER = UnitValue.CENTER;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue TRAILING = UnitValue.TRAILING;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue RIGHT = UnitValue.RIGHT;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue BOTTOM = UnitValue.BOTTOM;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue LABEL = UnitValue.LABEL;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue INF = UnitValue.INF;
+
+	/** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}.
+	 */
+	public static final UnitValue BASELINE_IDENTITY = UnitValue.BASELINE_IDENTITY;
+
+	private final static String[] X_Y_STRINGS = new String[] {"x", "y", "x2", "y2"};
+
+	/** Returns the version of IDEUtil
+	 * @return The version.
+	 */
+	public String getIDEUtilVersion()
+	{
+		return "1.0";
+	}
+
+	/** Returns the grid cells that the components in <code>parentContainer</code> has.
+	 * @param parentContainer The parent container. It is an object since MigLayout is GUI toolkit
+	 * independent.
+	 * @return A new hashmap with the components mapped to an array [x, y, spanx, spany].
+	 * <p>
+	 * Dock components will always have x and y less than -30000 or more than 30000. This is since they
+	 * are actually part of the grid, but on the outer edges.
+	 * <p>
+	 * Components that span the "rest of the row/column" have really large span values. Actually 30000-x or
+	 * 30000-y.
+	 * <p>
+	 * Generally, the grid does not need to have the upper left at 0, 0. Though it normally does if you
+	 * don't set the cells explicitly to other values. Rows and columns that are completely empty and
+	 * that does not have an explicit row/column constraint will be totally disregarded.
+	 */
+	public static HashMap<Object, int[]> getGridPositions(Object parentContainer)
+	{
+		return Grid.getGridPositions(parentContainer);
+	}
+
+	/** Returns the sizes of the rows and gaps for a container.
+	 * There will be two arrays returned [0] and [1].
+	 * <p>
+	 * The first array will be the indexes of the rows where indexes that
+	 * are less than 30000 or larger than 30000 is docking rows. There might be extra docking rows that aren't
+	 * visible but they always have size 0. Non docking indexes will probably always be 0, 1, 2, 3, etc..
+	 * <p>
+	 * The second array is the sizes of the form:<br>
+	 * <code>[left inset][row size 1][gap 1][row size 2][gap 2][row size n][right inset]</code>.
+	 * <p>
+	 * The returned sizes will be the ones calculated in the last layout cycle.
+	 * @param parentContainer The container to retuern the row sizes and gaps for. In Swing it will be a {@link java.awt.Container} and
+	 * in SWT it will be a {@link org.eclipse.swt.widgets.Composite}.
+	 * @return The sizes or <code>null</code> if {@link LayoutUtil#isDesignTime(ContainerWrapper)} is <code>false</code> or
+	 * <code>parentContainer</code> does not have a MigLayout layout manager.
+	 * The returned sizes will be the ones calculated in the last layout cycle.
+	 * @see LayoutUtil#isDesignTime(ContainerWrapper)
+	 */
+	public static int[][] getRowSizes(Object parentContainer)
+	{
+		return Grid.getSizesAndIndexes(parentContainer, true);
+	}
+
+	/** Returns the sizes of the columns and gaps for a container.
+	 * There will be two arrays returned [0] and [1].
+	 * <p>
+	 * The first array will be the indexes of the columns where indexes that
+	 * are less than 30000 or larger than 30000 is docking columns. There might be extra docking columns that aren't
+	 * visible but they always have size 0. Non docking indexes will probably always be 0, 1, 2, 3, etc..
+	 * <p>
+	 * The second array is the sizes of the form:<br>
+	 * <code>[top inset][column size 1][gap 1][column size 2][gap 2][column size n][bottom inset]</code>.
+	 * <p>
+	 * The returned sizes will be the ones calculated in the last layout cycle.
+	 * @param parentContainer The container to retuern the column sizes and gaps for. In Swing it will be a {@link java.awt.Container} and
+	 * in SWT it will be a {@link org.eclipse.swt.widgets.Composite}.
+	 * @return The sizes and indexes or <code>null</code> if {@link LayoutUtil#isDesignTime(ContainerWrapper)} is <code>false</code> or
+	 * <code>parentContainer</code> does not have a MigLayout layout manager.
+	 * The returned sizes will be the ones calculated in the last layout cycle.
+	 * @see LayoutUtil#isDesignTime(ContainerWrapper)
+	 */
+	public static int[][] getColumnSizes(Object parentContainer)
+	{
+		return Grid.getSizesAndIndexes(parentContainer, false);
+	}
+
+	/** Returns the a constraint string that can be re-parsed to be the exact same AxisConstraint.
+	 * @param ac The axis constraint to return as a constraint string.
+	 * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
+	 * as a String type (e.g. "flowx, gap rel, right").
+	 * @param isCols The the constraint should be returned for columns rather than rows.
+	 * @return A String. Never <code>null</code>.
+	 */
+	public static String getConstraintString(AC ac, boolean asAPI, boolean isCols)
+	{
+		StringBuffer sb = new StringBuffer(32);
+
+		DimConstraint[] dims = ac.getConstaints();
+		BoundSize defGap = isCols ? PlatformDefaults.getGridGapX() : PlatformDefaults.getGridGapY();
+
+		for (int i = 0; i < dims.length; i++) {
+			DimConstraint dc = dims[i];
+
+			addRowDimConstraintString(dc, sb, asAPI);
+
+			if (i < dims.length - 1) {
+				BoundSize gap = dc.getGapAfter();
+
+				if (gap == defGap || gap == null)
+					gap = dims[i + 1].getGapBefore();
+
+				if (gap != null) {
+					String gapStr = getBS(gap);
+					if (asAPI) {
+						sb.append(".gap(\"").append(gapStr).append("\")");
+					} else {
+						sb.append(gapStr);
+					}
+				} else {
+					if (asAPI)
+						sb.append(".gap()");
+				}
+			}
+		}
+
+		return sb.toString();
+	}
+
+	/** Adds the a constraint string that can be re-parsed to be the exact same DimConstraint.
+	 * @param dc The layout constraint to return as a constraint string.
+	 * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
+	 * as a String type (e.g. "flowx, gap rel, right").
+	 */
+	private static void addRowDimConstraintString(DimConstraint dc, StringBuffer sb, boolean asAPI)
+	{
+		int gp = dc.getGrowPriority();
+
+		int firstComma = sb.length();
+
+		BoundSize size = dc.getSize();
+		if (size.isUnset() == false) {
+			if (asAPI) {
+				sb.append(".size(\"").append(getBS(size)).append("\")");
+			} else {
+				sb.append(',').append(getBS(size));
+			}
+		}
+
+		if (gp != 100) {
+			if (asAPI) {
+				sb.append(".growPrio(").append(gp).append("\")");
+			} else {
+				sb.append(",growprio ").append(gp);
+			}
+		}
+
+		Float gw = dc.getGrow();
+		if (gw != null) {
+			String g = gw != 100f ? floatToString(gw, asAPI) : "";
+			if (asAPI) {
+				if (g.length() == 0) {
+					sb.append(".grow()");
+				} else {
+					sb.append(".grow(\"").append(g).append("\")");
+				}
+			} else {
+				sb.append(",grow").append(g.length() > 0 ? (" " + g) : "");
+			}
+		}
+
+		int sp = dc.getShrinkPriority();
+		if (sp != 100) {
+			if (asAPI) {
+				sb.append(".shrinkPrio(").append(sp).append("\")");
+			} else {
+				sb.append(",shrinkprio ").append(sp);
+			}
+		}
+
+		Float sw = dc.getShrink();
+		if (sw != null && sw.intValue() != 100) {
+			String s = floatToString(sw, asAPI);
+			if (asAPI) {
+				sb.append(".shrink(\"").append(s).append("\")");
+			} else {
+				sb.append(",shrink ").append(s);
+			}
+		}
+
+		String eg = dc.getEndGroup();
+		if (eg != null) {
+			if (asAPI) {
+				sb.append(".endGroup(\"").append(eg).append("\")");
+			} else {
+				sb.append(",endgroup ").append(eg);
+			}
+		}
+
+		String sg = dc.getSizeGroup();
+		if (sg != null) {
+			if (asAPI) {
+				sb.append(".sizeGroup(\"").append(sg).append("\")");
+			} else {
+				sb.append(",sizegroup ").append(sg);
+			}
+		}
+
+		UnitValue al = dc.getAlign();
+		if (al != null) {
+			if (asAPI) {
+				sb.append(".align(\"").append(getUV(al)).append("\")");
+			} else {
+				String s = getUV(al);
+				String alKw = (s.equals("top") || s.equals("bottom") || s.equals("left") || s.equals("label") ||
+							   s.equals("leading") || s.equals("center") || s.equals("trailing") ||
+							   s.equals("right") || s.equals("baseline")) ? "" : "align ";
+				sb.append(',').append(alKw).append(s);
+			}
+		}
+
+		if (dc.isNoGrid()) {
+			if (asAPI) {
+				sb.append(".noGrid()");
+			} else {
+				sb.append(",nogrid");
+			}
+		}
+
+		if (dc.isFill()) {
+			if (asAPI) {
+				sb.append(".fill()");
+			} else {
+				sb.append(",fill");
+			}
+		}
+
+		if (asAPI == false) {
+			if (sb.length() > firstComma) {
+				sb.setCharAt(firstComma, '[');
+				sb.append(']');
+			} else {
+				sb.append("[]");
+			}
+		}
+	}
+
+	/** Returns the a constraint string that can be re-parsed to be the exact same DimConstraint.
+	 * @param dc The layout constraint to return as a constraint string.
+	 * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
+	 * as a String type (e.g. "flowx, gap rel, right").
+	 * @param isHor The the DimConstraint is decoration something horizontal (column or x).
+	 * @param noGrowAdd If <code>true</code> no grow constraints will be added.
+	 * @return A constraint string. Never <code>null</code>.
+	 */
+	private static void addComponentDimConstraintString(DimConstraint dc, StringBuffer sb, boolean asAPI, boolean isHor, boolean noGrowAdd)
+	{
+		int gp = dc.getGrowPriority();
+		if (gp != 100) {
+			if (asAPI) {
+				sb.append(isHor ? ".growPrioX(" : ".growPrioY(").append(gp).append(')');
+			} else {
+				sb.append(isHor ? ",growpriox " : ",growprioy ").append(gp);
+			}
+		}
+
+		if (noGrowAdd == false) {
+			Float gw = dc.getGrow();
+			if (gw != null) {
+				String g = gw != 100f ? floatToString(gw, asAPI) : "";
+				if (asAPI) {
+					sb.append(isHor ? ".growX(" : ".growY(").append(g).append(')');
+				} else {
+					sb.append(isHor ? ",growx" : ",growy").append(g.length() > 0 ? (" " + g) : "");
+				}
+			}
+		}
+
+		int sp = dc.getShrinkPriority();
+		if (sp != 100) {
+			if (asAPI) {
+				sb.append(isHor ? ".shrinkPrioX(" : ".shrinkPrioY(").append(sp).append(')');
+			} else {
+				sb.append(isHor ? ",shrinkpriox " : ",shrinkprioy ").append(sp);
+			}
+		}
+
+		Float sw = dc.getShrink();
+		if (sw != null && sw.intValue() != 100) {
+			String s = floatToString(sw, asAPI);
+			if (asAPI) {
+				sb.append(isHor ? ".shrinkX(" : ".shrinkY(").append(s).append(')');
+			} else {
+				sb.append(isHor ? ",shrinkx " : ",shrinky ").append(s);
+			}
+		}
+
+		String eg = dc.getEndGroup();
+		if (eg != null) {
+			if (asAPI) {
+				sb.append(isHor ? ".endGroupX(\"" : ".endGroupY(\"").append(eg).append("\")");
+			} else {
+				sb.append(isHor ? ",endgroupx " : ",endgroupy ").append(eg);
+			}
+		}
+
+		String sg = dc.getSizeGroup();
+		if (sg != null) {
+			if (asAPI) {
+				sb.append(isHor ? ".sizeGroupX(\"" : ".sizeGroupY(\"").append(sg).append("\")");
+			} else {
+				sb.append(isHor ? ",sizegroupx " : ",sizegroupy ").append(sg);
+			}
+		}
+
+		appendBoundSize(dc.getSize(), sb, isHor, asAPI);
+
+		UnitValue al = dc.getAlign();
+		if (al != null) {
+			if (asAPI) {
+				sb.append(isHor ? ".alignX(\"" : ".alignY(\"").append(getUV(al)).append("\")");
+			} else {
+				sb.append(isHor ? ",alignx " : ",aligny ").append(getUV(al));
+			}
+		}
+
+		BoundSize gapBef = dc.getGapBefore();
+		BoundSize gapAft= dc.getGapAfter();
+		if (gapBef != null || gapAft != null) {
+			if (asAPI) {
+				sb.append(isHor ? ".gapX(\"" : ".gapY(\"").append(getBS(gapBef)).append("\", \"").append(getBS(gapAft)).append("\")");
+			} else {
+				sb.append(isHor ? ",gapx " : ",gapy ").append(getBS(gapBef));
+				if (gapAft != null)
+					sb.append(' ').append(getBS(gapAft));
+			}
+		}
+	}
+
+	private static void appendBoundSize(BoundSize size, StringBuffer sb, boolean isHor, boolean asAPI)
+	{
+		if (size.isUnset() == false) {
+			if (size.getPreferred() == null) {
+				if (size.getMin() == null) {
+					if (asAPI) {
+						sb.append(isHor ? ".maxWidth(\"" : ".maxHeight(\"").append(getUV(size.getMax())).append("\")");
+					} else {
+						sb.append(isHor ? ",wmax " : ",hmax ").append(getUV(size.getMax()));
+					}
+
+				} else if (size.getMax() == null) {
+					if (asAPI) {
+						sb.append(isHor ? ".minWidth(\"" : ".minHeight(\"").append(getUV(size.getMin())).append("\")");
+					} else {
+						sb.append(isHor ? ",wmin " : ",hmin ").append(getUV(size.getMin()));
+					}
+				} else { // None are null
+					if (asAPI) {
+						sb.append(isHor ? ".width(\"" : ".height(\"").append(getUV(size.getMin())).append("::").append(getUV(size.getMax())).append("\")");
+					} else {
+						sb.append(isHor ? ",width " : ",height ").append(getUV(size.getMin())).append("::").append(getUV(size.getMax()));
+					}
+				}
+			} else {
+				if (asAPI) {
+					sb.append(isHor ? ".width(\"" : ".height(\"").append(getBS(size)).append("\")");
+				} else {
+					sb.append(isHor ? ",width " : ",height ").append(getBS(size));
+				}
+			}
+		}
+	}
+
+	/** Returns the a constraint string that can be re-parsed to be the exact same LayoutConstraint.
+	 * @param cc The component constraint to return as a constraint string.
+	 * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
+	 * as a String type (e.g. "flowx, gap rel, right").
+	 * @return A String. Never <code>null</code>.
+	 */
+	public static String getConstraintString(CC cc, boolean asAPI)
+	{
+		StringBuffer sb = new StringBuffer(16);
+
+		if (cc.isNewline())
+			sb.append(asAPI ? ".newline()" : ",newline");
+
+		if (cc.isExternal())
+			sb.append(asAPI ? ".external()" : ",external");
+
+		Boolean flowX = cc.getFlowX();
+		if (flowX != null) {
+			if (asAPI) {
+				sb.append(flowX ? ".flowX()" : ".flowY()");
+			} else {
+				sb.append(flowX ? ",flowx" : ",flowy");
+			}
+		}
+
+		UnitValue[] pad = cc.getPadding();
+		if (pad != null) {
+			sb.append(asAPI ? ".pad(\"" : ",pad ");
+			for (int i = 0; i < pad.length; i++)
+				sb.append(getUV(pad[i])).append(i < pad.length - 1 ? " " : "");
+			if (asAPI)
+				sb.append("\")");
+		}
+
+		UnitValue[] pos = cc.getPos();
+		if (pos != null) {
+			if (cc.isBoundsInGrid()) {
+				for (int i = 0; i < 4; i++) {
+					if (pos[i] != null) {
+						if (asAPI) {
+							sb.append('.').append(X_Y_STRINGS[i]).append("(\"").append(getUV(pos[i])).append("\")");
+						} else {
+							sb.append(',').append(X_Y_STRINGS[i]).append(getUV(pos[i]));
+						}
+					}
+				}
+			} else {
+				sb.append(asAPI ? ".pos(\"" : ",pos ");
+				int iSz = (pos[2] != null || pos[3] != null) ? 4 : 2;  // "pos x y" vs "pos x1 y1 x2 y2".
+				for (int i = 0; i < iSz; i++)
+					sb.append(getUV(pos[i])).append(i < iSz - 1 ? " " : "");
+
+				if (asAPI)
+					sb.append("\")");
+			}
+		}
+
+		String id = cc.getId();
+		if (id != null) {
+			if (asAPI) {
+				sb.append(".id(\"").append(id).append("\")");
+			} else {
+				sb.append(",id ").append(id);
+			}
+		}
+
+		String tag = cc.getTag();
+		if (tag != null) {
+			if (asAPI) {
+				sb.append(".tag(\"").append(tag).append("\")");
+			} else {
+				sb.append(",tag ").append(tag);
+			}
+		}
+
+		int hideMode = cc.getHideMode();
+		if (hideMode >= 0) {
+			if (asAPI) {
+				sb.append(".hidemode(").append(hideMode).append(')');
+			} else {
+				sb.append(",hidemode ").append(hideMode);
+			}
+		}
+
+		int skip = cc.getSkip();
+		if (skip > 0) {
+			if (asAPI) {
+				sb.append(".skip(").append(skip).append(')');
+			} else {
+				sb.append(",skip ").append(skip);
+			}
+		}
+
+		int split = cc.getSplit();
+		if (split > 1) {
+			String s = split == LayoutUtil.INF ? "" : String.valueOf(split);
+			if (asAPI) {
+				sb.append(".split(").append(s).append(')');
+			} else {
+				sb.append(",split ").append(s);
+			}
+		}
+
+		int cx = cc.getCellX();
+		int cy = cc.getCellY();
+		int spanX = cc.getSpanX();
+		int spanY = cc.getSpanY();
+		if (cx >= 0 && cy >= 0) {
+			if (asAPI) {
+				sb.append(".cell(").append(cx).append(", ").append(cy);
+				if (spanX > 1 || spanY > 1)
+					sb.append(", ").append(spanX).append(", ").append(spanY);
+				sb.append(')');
+			} else {
+				sb.append(",cell ").append(cx).append(' ').append(cy);
+				if (spanX > 1 || spanY > 1)
+					sb.append(' ').append(spanX).append(' ').append(spanY);
+			}
+		} else if (spanX > 1 || spanY > 1) {
+			if (spanX > 1 && spanY > 1) {
+				sb.append(asAPI ? ".span(" : ",span ").append(spanX).append(asAPI ? ", " : " ").append(spanY);
+			} else if (spanX > 1) {
+				sb.append(asAPI ? ".spanX(" : ",spanx ").append(spanX == LayoutUtil.INF ? "" : (String.valueOf(spanX)));
+			} else if (spanY > 1) {
+				sb.append(asAPI ? ".spanY(" : ",spany ").append(spanY == LayoutUtil.INF ? "" : (String.valueOf(spanY)));
+			}
+			if (asAPI)
+				sb.append(')');
+		}
+
+		Float pushX = cc.getPushX();
+		Float pushY = cc.getPushY();
+		if (pushX != null || pushY != null) {
+			if (pushX != null && pushY != null) {
+				sb.append(asAPI ? ".push(" : ",push ");
+				if (pushX != 100.0 || pushY != 100.0)
+					sb.append(pushX).append(asAPI ? ", " : " ").append(pushY);
+			} else if (pushX != null) {
+				sb.append(asAPI ? ".pushX(" : ",pushx ").append(pushX == 100 ? "" : (String.valueOf(pushX)));
+			} else if (pushY != null) {
+				sb.append(asAPI ? ".pushY(" : ",pushy ").append(pushY == 100 ? "" : (String.valueOf(pushY)));
+			}
+			if (asAPI)
+				sb.append(')');
+		}
+
+		int dock = cc.getDockSide();
+		if (dock >= 0) {
+			String ds = CC.DOCK_SIDES[dock];
+			if (asAPI) {
+				sb.append(".dock").append(Character.toUpperCase(ds.charAt(0))).append(ds.substring(1)).append("()");
+			} else {
+				sb.append(",").append(ds);
+			}
+		}
+
+		boolean noGrowAdd = cc.getHorizontal().getGrow() != null && cc.getHorizontal().getGrow().intValue() == 100 &&
+		                    cc.getVertical().getGrow() != null && cc.getVertical().getGrow().intValue() == 100;
+
+		addComponentDimConstraintString(cc.getHorizontal(), sb, asAPI, true, noGrowAdd);
+		addComponentDimConstraintString(cc.getVertical(), sb, asAPI, false, noGrowAdd);
+		if (noGrowAdd)
+			sb.append(asAPI ? ".grow()" : ",grow");   // Combine ".growX().growY()" into ".grow()".
+
+		if (cc.isWrap())
+			sb.append(asAPI ? ".wrap()" : ",wrap");
+
+		String s = sb.toString();
+		return s.length() == 0 || s.charAt(0) != ',' ? s : s.substring(1);
+	}
+
+	/** Returns the a constraint string that can be re-parsed to be the exact same LayoutConstraint.
+	 * @param lc The layout constraint to return as a constraint string.
+	 * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
+	 * as a String type (e.g. "flowx, gap rel, right").
+	 * @return A String. Never <code>null</code>.
+	 */
+	public static String getConstraintString(LC lc, boolean asAPI)
+	{
+		StringBuffer sb = new StringBuffer(16);
+
+		if (lc.isFlowX() == false)
+			sb.append(asAPI ? ".flowY()" : ",flowy");
+
+		boolean fillX = lc.isFillX();
+		boolean fillY = lc.isFillY();
+		if (fillX || fillY) {
+			if (fillX == fillY) {
+				sb.append(asAPI ? ".fill()" : ",fill");
+			} else {
+				sb.append(asAPI ? (fillX ? ".fillX()" : ".fillY()") : (fillX ? ",fillx" : ",filly"));
+			}
+		}
+
+		Boolean leftToRight = lc.getLeftToRight();
+		if (leftToRight != null) {
+			if (asAPI) {
+				sb.append(".leftToRight(").append(leftToRight).append(')');
+			} else {
+				sb.append(leftToRight ? ",ltr" : ",rtl");
+			}
+		}
+
+		if (!lc.getPackWidth().isUnset() || !lc.getPackHeight().isUnset()) {
+			if (asAPI) {
+				String w = getBS(lc.getPackWidth());
+				String h = getBS(lc.getPackHeight());
+				sb.append(".pack(");
+				if (w.equals("pref") && h.equals("pref")) {
+					sb.append(')');
+				} else {
+					sb.append('\"').append(w).append("\", \"").append(h).append("\")");
+				}
+			} else {
+				sb.append(",pack");
+				String size = getBS(lc.getPackWidth()) + " " + getBS(lc.getPackHeight());
+				if (size.equals("pref pref") == false)
+					sb.append(' ').append(size);
+			}
+		}
+
+		if (lc.getPackWidthAlign() != 0.5f || lc.getPackHeightAlign() != 1f) {
+			if (asAPI) {
+				sb.append(".packAlign(").append(floatToString(lc.getPackWidthAlign(), asAPI)).append(", ").append(floatToString(lc.getPackHeightAlign(), asAPI)).append(')');
+			} else {
+				sb.append(",packalign ").append(floatToString(lc.getPackWidthAlign(), asAPI)).append(' ').append(floatToString(lc.getPackHeightAlign(), asAPI));
+			}
+		}
+
+		if (lc.isTopToBottom() == false)
+			sb.append(asAPI ? ".bottomToTop()" : ",btt");
+
+		UnitValue[] insets = lc.getInsets();
+		if (insets != null) {
+			String cs = LayoutUtil.getCCString(insets);
+			if (cs != null) {
+				if (asAPI) {
+					sb.append(".insets(\"").append(cs).append("\")");
+				} else {
+					sb.append(",insets ").append(cs);
+				}
+			} else {
+				sb.append(asAPI ? ".insets(\"" : ",insets ");
+				for (int i = 0; i < insets.length; i++)
+					sb.append(getUV(insets[i])).append(i < insets.length - 1 ? " " : "");
+				if (asAPI)
+					sb.append("\")");
+			}
+		}
+
+		if (lc.isNoGrid())
+			sb.append(asAPI ? ".noGrid()" : ",nogrid");
+
+		if (lc.isVisualPadding() == false)
+			sb.append(asAPI ? ".noVisualPadding()" : ",novisualpadding");
+
+		int hideMode = lc.getHideMode();
+		if (hideMode > 0) {
+			if (asAPI) {
+				sb.append(".hideMode(").append(hideMode).append(')');
+			} else {
+				sb.append(",hideMode ").append(hideMode);
+			}
+		}
+
+		appendBoundSize(lc.getWidth(), sb, true, asAPI);
+		appendBoundSize(lc.getHeight(), sb, false, asAPI);
+
+		UnitValue alignX = lc.getAlignX();
+		UnitValue alignY = lc.getAlignY();
+		if (alignX != null || alignY != null) {
+			if (alignX != null && alignY != null) {
+				sb.append(asAPI ? ".align(\"" : ",align ").append(getUV(alignX)).append(' ').append(getUV(alignY));
+			} else if (alignX != null) {
+				sb.append(asAPI ? ".alignX(\"" : ",alignx ").append(getUV(alignX));
+			} else if (alignY != null) {
+				sb.append(asAPI ? ".alignY(\"" : ",aligny ").append(getUV(alignY));
+			}
+			if (asAPI)
+				sb.append("\")");
+		}
+
+		BoundSize gridGapX = lc.getGridGapX();
+		BoundSize gridGapY = lc.getGridGapY();
+		if (gridGapX != null || gridGapY != null) {
+			if (gridGapX != null && gridGapY != null) {
+				sb.append(asAPI ? ".gridGap(\"" : ",gap ").append(getBS(gridGapX)).append(' ').append(getBS(gridGapY));
+			} else if (gridGapX != null) {
+				sb.append(asAPI ? ".gridGapX(\"" : ",gapx ").append(getBS(gridGapX));
+			} else if (gridGapY != null) {
+				sb.append(asAPI ? ".gridGapY(\"" : ",gapy ").append(getBS(gridGapY));
+			}
+			if (asAPI)
+				sb.append("\")");
+		}
+
+		int wrapAfter = lc.getWrapAfter();
+		if (wrapAfter != LayoutUtil.INF) {
+			String ws = wrapAfter > 0 ? String.valueOf(wrapAfter) : "";
+			if (asAPI) {
+				sb.append(".wrap(").append(ws).append(')');
+			} else {
+				sb.append(",wrap ").append(ws);
+			}
+		}
+
+		int debugMillis = lc.getDebugMillis();
+		if (debugMillis > 0) {
+			if (asAPI) {
+				sb.append(".debug(").append(debugMillis).append(')');
+			} else {
+				sb.append(",debug ").append(debugMillis);
+			}
+		}
+
+		String s = sb.toString();
+		return s.length() == 0 || s.charAt(0) != ',' ? s : s.substring(1);
+	}
+
+	private static String getUV(UnitValue uv)
+	{
+		return uv != null ? uv.getConstraintString() : "null";
+	}
+
+	private static String getBS(BoundSize bs)
+	{
+		return bs != null ? bs.getConstraintString() : "null";
+	}
+
+	/** Converts a <code>float</code> to a string and is removing the ".0" if the float is an integer.
+	 * @param f the float.
+	 * @return <code>f</code> as a string. Never <code>null</code>.
+	 */
+	private static String floatToString(float f, boolean asAPI)
+	{
+		String valS = String.valueOf(f);
+		return valS.endsWith(".0") ? valS.substring(0, valS.length() - 2) : (valS + (asAPI ? "f" : ""));
+	}
+}
diff --git a/src/net/miginfocom/layout/InCellGapProvider.java b/src/net/miginfocom/layout/InCellGapProvider.java
new file mode 100644
index 0000000..ebb548d
--- /dev/null
+++ b/src/net/miginfocom/layout/InCellGapProvider.java
@@ -0,0 +1,65 @@
+package net.miginfocom.layout;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** An interface to implement if you want to decide the gaps between two types of components within the same cell.
+ * <p>
+ * E.g.:
+ *
+ * <pre>
+ * if (adjacentComp == null || adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.TOP)
+ *	  return null;
+ *
+ * boolean isHor = (adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.RIGHT);
+ *
+ * if (adjacentComp.getComponentType(false) == ComponentWrapper.TYPE_LABEL && comp.getComponentType(false) == ComponentWrapper.TYPE_TEXT_FIELD)
+ *    return isHor ? UNRELATED_Y : UNRELATED_Y;
+ *
+ * return (adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.RIGHT) ? RELATED_X : RELATED_Y;
+ * </pre
+ */
+public interface InCellGapProvider
+{
+	/** Returns the default gap between two components that <b>are in the same cell</b>.
+	 * @param comp The component that the gap is for. Never <code>null</code>.
+	 * @param adjacentComp The adjacent component if any. May be <code>null</code>.
+	 * @param adjacentSide What side the <code>adjacentComp</code> is on. {@link javax.swing.SwingUtilities#TOP} or
+	 * {@link javax.swing.SwingUtilities#LEFT} or {@link javax.swing.SwingUtilities#BOTTOM} or {@link javax.swing.SwingUtilities#RIGHT}.
+	 * @param tag The tag string that the component might be tagged with in the component constraints. May be <code>null</code>.
+	 * @param isLTR If it is left-to-right.
+	 * @return The default gap between two components or <code>null</code> if there should be no gap.
+	 */
+	public abstract BoundSize getDefaultGap(ComponentWrapper comp, ComponentWrapper adjacentComp, int adjacentSide, String tag, boolean isLTR);
+}
diff --git a/src/net/miginfocom/layout/LC.java b/src/net/miginfocom/layout/LC.java
new file mode 100644
index 0000000..26e75d6
--- /dev/null
+++ b/src/net/miginfocom/layout/LC.java
@@ -0,0 +1,1032 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** Contains the constraints for an instance of the {@link LC} layout manager.
+ */
+public final class LC implements Externalizable
+{
+	// See the corresponding set/get method for documentation of the property!
+
+	private int wrapAfter = LayoutUtil.INF;
+
+	private Boolean leftToRight = null;
+
+	private UnitValue[] insets = null;    // Never null elememts but if unset array is null
+
+	private UnitValue alignX = null, alignY = null;
+
+	private BoundSize gridGapX = null, gridGapY = null;
+
+	private BoundSize width = BoundSize.NULL_SIZE, height = BoundSize.NULL_SIZE;
+
+	private BoundSize packW = BoundSize.NULL_SIZE, packH = BoundSize.NULL_SIZE;
+
+	private float pwAlign = 0.5f, phAlign = 1.0f;
+
+	private int debugMillis = 0;
+
+	private int hideMode = 0;
+
+	private boolean noCache = false;
+
+	private boolean flowX = true;
+
+	private boolean fillX = false, fillY = false;
+
+	private boolean topToBottom = true;
+
+	private boolean noGrid = false;
+
+	private boolean visualPadding = true;
+
+	/** Empty constructor.
+	 */
+	public LC()
+	{
+	}
+
+	// ************************************************************************
+	// * JavaBean get/set methods.
+	// ************************************************************************
+
+
+	/** If components have sizes or positions linked to the bounds of the parent in some way (as for instance the <code>"%"</code> unit has) the cache
+	 * must be turned off for the panel. If components does not get the correct or expected size or position try to set this property to <code>true</code>.
+	 * @return <code>true</code> means no cache and slightly slower layout.
+	 */
+	public boolean isNoCache()
+	{
+		return noCache;
+	}
+
+	/** If components have sizes or positions linked to the bounds of the parent in some way (as for instance the <code>"%"</code> unit has) the cache
+	 * must be turned off for the panel. If components does not get the correct or expected size or position try to set this property to <code>true</code>.
+	 * @param b <code>true</code> means no cache and slightly slower layout.
+	 */
+	public void setNoCache(boolean b)
+	{
+		this.noCache = b;
+	}
+
+	/** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+	 * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+	 * by this property.
+	 * @return The current alignment.
+	 */
+	public final UnitValue getAlignX()
+	{
+		return alignX;
+	}
+
+	/** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+	 * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+	 * by this property.
+	 * @param uv The new alignment. Use {@link ConstraintParser#parseAlignKeywords(String, boolean)} to create the {@link UnitValue}. May be <code>null</code>.
+	 */
+	public final void setAlignX(UnitValue uv)
+	{
+		this.alignX = uv;
+	}
+
+	/** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+	 * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+	 * by this property.
+	 * @return The current alignment.
+	 */
+	public final UnitValue getAlignY()
+	{
+		return alignY;
+	}
+
+	/** If the laid out components' bounds in total is less than the final size of the container these align values will be used to align the components
+	 * in the parent. <code>null</code> is default and that means top/left alignment. The relative distances between the components will not be affected
+	 * by this property.
+	 * @param uv The new alignment. Use {@link ConstraintParser#parseAlignKeywords(String, boolean)} to create the {@link UnitValue}. May be <code>null</code>.
+	 */
+	public final void setAlignY(UnitValue uv)
+	{
+		this.alignY = uv;
+	}
+
+	/** If <code>> 0</code> the debug decorations will be repainted every <code>millis</code>. No debug information if <code><= 0</code> (default).
+	 * @return The current debug repaint interval.
+	 */
+	public final int getDebugMillis()
+	{
+		return debugMillis;
+	}
+
+	/** If <code>> 0</code> the debug decorations will be repainted every <code>millis</code>. No debug information if <code><= 0</code> (default).
+	 * @param millis The new debug repaint interval.
+	 */
+	public final void setDebugMillis(int millis)
+	{
+		this.debugMillis = millis;
+	}
+
+	/** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+	 * @return <code>true</code> means fill. <code>false</code> is default.
+	 */
+	public final boolean isFillX()
+	{
+		return fillX;
+	}
+
+	/** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+	 * @param b <code>true</code> means fill. <code>false</code> is default.
+	 */
+	public final void setFillX(boolean b)
+	{
+		this.fillX = b;
+	}
+
+	/** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+	 * @return <code>true</code> means fill. <code>false</code> is default.
+	 */
+	public final boolean isFillY()
+	{
+		return fillY;
+	}
+
+	/** If the layout should always claim the whole bounds of the laid out container even if the preferred size is smaller.
+	 * @param b <code>true</code> means fill. <code>false</code> is default.
+	 */
+	public final void setFillY(boolean b)
+	{
+		this.fillY = b;
+	}
+
+	/** The default flow direction. Normally (which is <code>true</code>) this is horizontal and that means that the "next" component
+	 * will be put in the cell to the right (or to the left if left-to-right is false).
+	 * @return <code>true</code> is the default flow horizontally.
+	 * @see #setLeftToRight(Boolean)
+	 */
+	public final boolean isFlowX()
+	{
+		return flowX;
+	}
+
+	/** The default flow direction. Normally (which is <code>true</code>) this is horizontal and that means that the "next" component
+	 * will be put in the cell to the right (or to the left if left-to-right is false).
+	 * @param b <code>true</code> is the default flow horizontally.
+	 * @see #setLeftToRight(Boolean)
+	 */
+	public final void setFlowX(boolean b)
+	{
+		this.flowX = b;
+	}
+
+	/** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the columns in the grid.
+	 * @return The default grid gap between columns in the grid. <code>null</code> if the platform default is used.
+	 */
+	public final BoundSize getGridGapX()
+	{
+		return gridGapX;
+	}
+
+	/** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the columns in the grid.
+	 * @param x The default grid gap between columns in the grid. If <code>null</code> the platform default is used.
+	 */
+	public final void setGridGapX(BoundSize x)
+	{
+		this.gridGapX = x;
+	}
+
+	/** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the rows in the grid.
+	 * @return The default grid gap between rows in the grid. <code>null</code> if the platform default is used.
+	 */
+	public final BoundSize getGridGapY()
+	{
+		return gridGapY;
+	}
+
+	/** If non-<code>null</code> (<code>null</code> is default) these value will be used as the default gaps between the rows in the grid.
+	 * @param y The default grid gap between rows in the grid. If <code>null</code> the platform default is used.
+	 */
+	public final void setGridGapY(BoundSize y)
+	{
+		this.gridGapY = y;
+	}
+
+	/** How a component that is hidden (not visible) should be treated by default.
+	 * @return The mode:<br>
+	 * 0 == Normal. Bounds will be caclulated as if the component was visible.<br>
+	 * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+	 * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+	 * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+	 */
+	public final int getHideMode()
+	{
+		return hideMode;
+	}
+
+	/** How a component that is hidden (not visible) should be treated.
+	 * @param mode The mode:<br>
+	 * 0 == Normal. Bounds will be caclulated as if the component was visible.<br>
+	 * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+	 * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+	 * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+	 */
+	public final void setHideMode(int mode)
+	{
+		if (mode < 0 || mode > 3)
+			throw new IllegalArgumentException("Wrong hideMode: " + mode);
+
+		this.hideMode = mode;
+	}
+
+	/** The insets for the layed out panel. The insets will be an empty space around the components in the panel. <code>null</code> values
+	 * means that the default panel insets for the platform is used. See {@link PlatformDefaults#setDialogInsets(net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue)}.
+	 * @return The insets. Of length 4 (top, left, bottom, right) or <code>null</code>. The elements (1 to 4) may be <code>null</code>. The array is a copy and can be used freely.
+	 * @see net.miginfocom.layout.ConstraintParser#parseInsets(String, boolean)
+	 */
+	public final UnitValue[] getInsets()
+	{
+		return insets != null ? new UnitValue[] {insets[0], insets[1], insets[2], insets[3]} : null;
+	}
+
+	/** The insets for the layed out panel. The insets will be an empty space around the components in the panel. <code>null</code> values
+	 * means that the default panel insets for the platform is used. See {@link PlatformDefaults#setDialogInsets(net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue, net.miginfocom.layout.UnitValue)}.
+	 * @param ins The new insets. Must be of length 4 (top, left, bottom, right) or <code>null</code>. The elements (1 to 4) may be <code>null</code> to use
+	 * the platform default for that side. The array is copied for storage.
+	 * @see net.miginfocom.layout.ConstraintParser#parseInsets(String, boolean)
+	 */
+	public final void setInsets(UnitValue[] ins)
+	{
+		this.insets = ins != null ? new UnitValue[] {ins[0], ins[1], ins[2], ins[3]} : null;
+	}
+
+	/** If the layout should be forced to be left-to-right or right-to-left. A value of <code>null</code> is default and
+	 * means that this will be picked up from the {@link java.util.Locale} that the container being layed out is reporting.
+	 * @return <code>Boolean.TRUE</code> if force left-to-right. <code>Boolean.FALSE</code> if force tight-to-left. <code>null</code>
+	 * for the default "let the current Locale decide".
+	 */
+	public final Boolean getLeftToRight()
+	{
+		return leftToRight;
+	}
+
+	/** If the layout should be forced to be left-to-right or right-to-left. A value of <code>null</code> is default and
+	 * means that this will be picked up from the {@link java.util.Locale} that the container being layed out is reporting.
+	 * @param b <code>Boolean.TRUE</code> to force left-to-right. <code>Boolean.FALSE</code> to force tight-to-left. <code>null</code>
+	 * for the default "let the current Locale decide".
+	 */
+	public final void setLeftToRight(Boolean b)
+	{
+		this.leftToRight = b;
+	}
+
+	/** If the whole layout should be non grid based. It is the same as setting the "nogrid" property on every row/column in the grid.
+	 * @return <code>true</code> means not grid based. <code>false</code> is default.
+	 */
+	public final boolean isNoGrid()
+	{
+		return noGrid;
+	}
+
+	/** If the whole layout should be non grid based. It is the same as setting the "nogrid" property on every row/column in the grid.
+	 * @param b <code>true</code> means no grid. <code>false</code> is default.
+	 */
+	public final void setNoGrid(boolean b)
+	{
+		this.noGrid = b;
+	}
+
+	/** If the layout should go from the default top-to-bottom in the grid instead of the optinal bottom-to-top.
+	 * @return <code>true</code> for the default top-to-bottom.
+	 */
+	public final boolean isTopToBottom()
+	{
+		return topToBottom;
+	}
+
+	/** If the layout should go from the default top-to-bottom in the grid instead of the optinal bottom-to-top.
+	 * @param b <code>true</code> for the default top-to-bottom.
+	 */
+	public final void setTopToBottom(boolean b)
+	{
+		this.topToBottom = b;
+	}
+
+	/** If visual padding should be automatically used and compensated for by this layout instance.
+	 * @return <code>true</code> if visual padding.
+	 */
+	public final boolean isVisualPadding()
+	{
+		return visualPadding;
+	}
+
+	/** If visual padding should be automatically used and compensated for by this layout instance.
+	 * @param b <code>true</code> turns on visual padding.
+	 */
+	public final void setVisualPadding(boolean b)
+	{
+		this.visualPadding = b;
+	}
+
+	/** Returns after what cell the grid should always auto wrap.
+	 * @return After what cell the grid should always auto wrap. If <code>0</code> the number of columns/rows in the
+	 * {@link net.miginfocom.layout.AC} is used. <code>LayoutUtil.INF</code> is used for no auto wrap.
+	 */
+	public final int getWrapAfter()
+	{
+		return wrapAfter;
+	}
+
+	/** Sets after what cell the grid should always auto wrap.
+	 * @param count After what cell the grid should always auto wrap. If <code>0</code> the number of columns/rows in the
+	 * {@link net.miginfocom.layout.AC} is used. <code>LayoutUtil.INF</code> is used for no auto wrap.
+	 */
+	public final void setWrapAfter(int count)
+	{
+		this.wrapAfter = count;
+	}
+
+	/** Returns the "pack width" for the <b>window</b> that this container is located in. When the size of this container changes
+	 * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+	 * as well as the size window should optimally get. This optimal size is normaly its "preferred" size which is why "preferred"
+	 * is the normal value to set here.
+	 * <p>
+	 * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+	 * <p>
+	 * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+	 * @return The current value. Never <code>null</code>. Check if not set with <code>.isUnset()</code>.
+	 * @since 3.5
+	 */
+	public final BoundSize getPackWidth()
+	{
+		return packW;
+	}
+
+	/** Sets the "pack width" for the <b>window</b> that this container is located in. When the size of this container changes
+	 * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+	 * as well as the size window should optimally get. This optimal size is normaly its "preferred" size which is why "preferred"
+	 * is the normal value to set here.
+	 * <p>
+	 * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+	 * <p>
+	 * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+	 * @param size The new pack size. If <code>null</code> it will be corrected to an "unset" BoundSize.
+	 * @since 3.5
+	 */
+	public final void setPackWidth(BoundSize size)
+	{
+		packW = size != null ? size : BoundSize.NULL_SIZE;
+	}
+
+	/** Returns the "pack height" for the <b>window</b> that this container is located in. When the size of this container changes
+	 * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+	 * as well as the size window should optimally get. This optimal size is normaly its "preferred" size which is why "preferred"
+	 * is the normal value to set here.
+	 * <p>
+	 * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+	 * <p>
+	 * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+	 * @return The current value. Never <code>null</code>. Check if not set with <code>.isUnset()</code>.
+	 * @since 3.5
+	 */
+	public final BoundSize getPackHeight()
+	{
+		return packH;
+	}
+
+	/** Sets the "pack height" for the <b>window</b> that this container is located in. When the size of this container changes
+	 * the size of the window will be corrected to be within this BoundsSize. It can be used to set the minimum and/or maximum size of the window
+	 * as well as the size window should optimally get. This optimal size is normaly its "preferred" size which is why "preferred"
+	 * is the normal value to set here.
+	 * <p>
+	 * ":push" can be appended to the bound size to only push the size bigger and never shrink it if the preferred size gets smaller.
+	 * <p>
+	 * E.g. "pref", "100:pref", "pref:700", "300::700", "pref:push"
+	 * @param size The new pack size. If <code>null</code> it will be corrected to an "unset" BoundSize.
+	 * @since 3.5
+	 */
+	public final void setPackHeight(BoundSize size)
+	{
+		packH = size != null ? size : BoundSize.NULL_SIZE;
+	}
+
+
+	/** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+	 * decides where the extra/surpurflous size is placed. 0f means that the window will resize so that the upper part moves up and the
+	 * lower side stays in the same place. 0.5f will expand/reduce the window equally upwards and downwards. 1f will do the opposite of 0f
+	 * of course.
+	 * @return The pack alignment. Always between 0f and 1f, inclusive.
+	 * @since 3.5
+	 */
+	public final float getPackHeightAlign()
+	{
+		return phAlign;
+	}
+
+	/** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+	 * decides where the extra/surpurflous size is placed. 0f means that the window will resize so that the upper part moves up and the
+	 * lower side stays in the same place. 0.5f will expand/reduce the window equally upwards and downwards. 1f will do the opposite of 0f
+	 * of course.
+	 * @param align The pack alignment. Always between 0f and 1f, inclusive. Values outside this will be truncated.
+	 * @since 3.5
+	 */
+	public final void setPackHeightAlign(float align)
+	{
+		phAlign = Math.max(0f, Math.min(1f, align));
+	}
+
+	/** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+	 * decides where the extra/surpurflous size is placed. 0f means that the window will resize so that the left part moves left and the
+	 * right side stays in the same place. 0.5f will expand/reduce the window equally to the right and lefts. 1f will do the opposite of 0f
+	 * of course.
+	 * @return The pack alignment. Always between 0f and 1f, inclusive.
+	 * @since 3.5
+	 */
+	public final float getPackWidthAlign()
+	{
+		return pwAlign;
+	}
+
+	/** If there is a resize of the window due to packing (see {@link #setPackHeight(BoundSize)} this value, which is between 0f and 1f,
+	 * decides where the extra/surpurflous size is placed. 0f means that the window will resize so that the left part moves left and the
+	 * right side stays in the same place. 0.5f will expand/reduce the window equally to the right and lefts. 1f will do the opposite of 0f
+	 * of course.
+	 * @param align The pack alignment. Always between 0f and 1f, inclusive. Values outside this will be truncated.
+	 * @since 3.5
+	 */
+	public final void setPackWidthAlign(float align)
+	{
+		pwAlign = Math.max(0f, Math.min(1f, align));
+	}
+
+	/** Returns the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+	 * sizes that is not <code>null</code> will be returned directly instead of determining the correspondig size through
+	 * asking the components in this container.
+	 * @return The width for the container that this layout constraint is set for. Not <code>null</code> but
+	 * all sizes can be <code>null</code>.
+	 * @since 3.5
+	 */
+	public final BoundSize getWidth()
+	{
+		return width;
+	}
+
+	/** Sets the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+	 * sizes that is not <code>null</code> will be returned directly instead of determining the correspondig size through
+	 * asking the components in this container.
+	 * @param size The width for the container that this layout constraint is set for. <code>null</code> is translated to
+	 * a bound size containing only null sizes.
+	 * @since 3.5
+	 */
+	public final void setWidth(BoundSize size)
+	{
+		this.width = size != null ? size : BoundSize.NULL_SIZE;
+	}
+
+	/** Returns the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+	 * sizes that is not <code>null</code> will be returned directly instead of determining the correspondig size through
+	 * asking the components in this container.
+	 * @return The height for the container that this layout constraint is set for. Not <code>null</code> but
+	 * all sizes can be <code>null</code>.
+	 * @since 3.5
+	 */
+	public final BoundSize getHeight()
+	{
+		return height;
+	}
+
+	/** Sets the minimum/preferred/maximum size for the container that this layout constraint is set for. Any of these
+	 * sizes that is not <code>null</code> will be returned directly instead of determining the correspondig size through
+	 * asking the components in this container.
+	 * @param size The height for the container that this layout constraint is set for. <code>null</code> is translated to
+	 * a bound size containing only null sizes.
+	 * @since 3.5
+	 */
+	public final void setHeight(BoundSize size)
+	{
+		this.height = size != null ? size : BoundSize.NULL_SIZE;
+	}
+
+	// ************************************************************************
+	// * Builder methods.
+	// ************************************************************************
+
+	/** Short for, and thus same as, <code>.pack("pref", "pref")</code>.
+	 * <p>
+	 * Same functionality as {@link #setPackHeight(BoundSize)} and {@link #setPackWidth(net.miginfocom.layout.BoundSize)}
+	 * only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.5
+	 */
+	public final LC pack()
+	{
+		return pack("pref", "pref");
+	}
+
+	/** Sets the pack width and height.
+	 * <p>
+	 * Same functionality as {@link #setPackHeight(BoundSize)} and {@link #setPackWidth(net.miginfocom.layout.BoundSize)}
+	 * only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param width The pack width. May be <code>null</code>.
+	 * @param height The pack height. May be <code>null</code>.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.5
+	 */
+	public final LC pack(String width, String height)
+	{
+		setPackWidth(width != null ? ConstraintParser.parseBoundSize(width, false, false) : BoundSize.NULL_SIZE);
+		setPackHeight(height != null ? ConstraintParser.parseBoundSize(height, false, false) : BoundSize.NULL_SIZE);
+		return this;
+	}
+
+	/** Sets the pack width and height alignment.
+	 * <p>
+	 * Same functionality as {@link #setPackHeightAlign(float)} and {@link #setPackWidthAlign(float)}
+	 * only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param alignX The pack width alignment. 0.5f is default.
+	 * @param alignY The pack height alignment. 0.5f is default.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.5
+	 */
+	public final LC packAlign(float alignX, float alignY)
+	{
+		setPackWidthAlign(alignX);
+		setPackHeightAlign(alignY);
+		return this;
+	}
+
+	/** Sets a wrap after the number of columns/rows that is defined in the {@link net.miginfocom.layout.AC}.
+	 * <p>
+	 * Same functionality as {@link #setWrapAfter(int 0)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC wrap()
+	{
+		setWrapAfter(0);
+		return this;
+	}
+
+	/** Same functionality as {@link #setWrapAfter(int)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param count After what cell the grid should always auto wrap. If <code>0</code> the number of columns/rows in the
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC wrapAfter(int count)
+	{
+		setWrapAfter(count);
+		return this;
+	}
+
+	/** Same functionality as {@link #setNoCache(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC noCache()
+	{
+		setNoCache(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setFlowX(boolean false)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC flowY()
+	{
+		setFlowX(false);
+		return this;
+	}
+
+	/** Same functionality as {@link #setFlowX(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC flowX()
+	{
+		setFlowX(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setFillX(boolean true)} and {@link #setFillY(boolean true)} conmbined.T his method returns
+	 * <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC fill()
+	{
+		setFillX(true);
+		setFillY(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setFillX(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC fillX()
+	{
+		setFillX(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setFillY(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC fillY()
+	{
+		setFillY(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setLeftToRight(Boolean)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param b <code>true</code> for forcing left-to-right. <code>false</code> for forcing right-to-left.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC leftToRight(boolean b)
+	{
+		setLeftToRight(b ? Boolean.TRUE : Boolean.FALSE);
+		return this;
+	}
+
+	/** Same functionality as setLeftToRight(false) only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final LC rightToLeft()
+	{
+		setLeftToRight(Boolean.FALSE);
+		return this;
+	}
+
+	/** Same functionality as {@link #setTopToBottom(boolean false)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC bottomToTop()
+	{
+		setTopToBottom(false);
+		return this;
+	}
+
+	/** Same functionality as {@link #setTopToBottom(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @since 3.7.2
+	 */
+	public final LC topToBottom()
+	{
+		setTopToBottom(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setNoGrid(boolean true)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC noGrid()
+	{
+		setNoGrid(true);
+		return this;
+	}
+
+	/** Same functionality as {@link #setVisualPadding(boolean false)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC noVisualPadding()
+	{
+		setVisualPadding(false);
+		return this;
+	}
+
+	/** Sets the same inset (expressed as a <code>UnitValue</code>, e.g. "10px" or "20mm") all around.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param allSides The unit value to set for all sides. May be <code>null</code> which means that the default panel insets
+	 * for the platform is used.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setInsets(UnitValue[])
+	 */
+	public final LC insetsAll(String allSides)
+	{
+		UnitValue insH = ConstraintParser.parseUnitValue(allSides, true);
+		UnitValue insV = ConstraintParser.parseUnitValue(allSides, false);
+		insets = new UnitValue[] {insV, insH, insV, insH}; // No setter to avoid copy again
+		return this;
+	}
+
+	/** Same functionality as <code>setInsets(ConstraintParser.parseInsets(s, true))</code>. This method returns <code>this</code>
+	 * for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param s The string to parse. E.g. "10 10 10 10" or "20". If less than 4 groups the last will be used for the missing.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setInsets(UnitValue[])
+	 */
+	public final LC insets(String s)
+	{
+		insets = ConstraintParser.parseInsets(s, true);
+		return this;
+	}
+
+	/** Sets the different insets (expressed as a <code>UnitValue</code>s, e.g. "10px" or "20mm") for the corresponding sides.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param top The top inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+	 * side for the platform will be used.
+	 * @param left The left inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+	 * side for the platform will be used.
+	 * @param bottom The bottom inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+	 * side for the platform will be used.
+	 * @param right The right inset. E.g. "10px" or "10mm" or "related". May be <code>null</code> in which case the default inset for this
+	 * side for the platform will be used.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setInsets(UnitValue[])
+	 */
+	public final LC insets(String top, String left, String bottom, String right)
+	{
+		insets = new UnitValue[] { // No setter to avoid copy again
+				ConstraintParser.parseUnitValue(top, false),
+				ConstraintParser.parseUnitValue(left, true),
+				ConstraintParser.parseUnitValue(bottom, false),
+				ConstraintParser.parseUnitValue(right, true)};
+		return this;
+	}
+
+	/** Same functionality as <code>setAlignX(ConstraintParser.parseUnitValueOrAlign(unitValue, true))</code> only this method returns <code>this</code>
+	 * for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param align The align keyword or for instance "100px". E.g "left", "right", "leading" or "trailing".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setAlignX(UnitValue)
+	 */
+	public final LC alignX(String align)
+	{
+		setAlignX(ConstraintParser.parseUnitValueOrAlign(align, true, null));
+		return this;
+	}
+
+	/** Same functionality as <code>setAlignY(ConstraintParser.parseUnitValueOrAlign(align, false))</code> only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param align The align keyword or for instance "100px". E.g "top" or "bottom".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setAlignY(UnitValue)
+	 */
+	public final LC alignY(String align)
+	{
+		setAlignY(ConstraintParser.parseUnitValueOrAlign(align, false, null));
+		return this;
+	}
+
+	/** Sets both the alignX and alignY as the same time.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param ax The align keyword or for instance "100px". E.g "left", "right", "leading" or "trailing".
+	 * @param ay The align keyword or for instance "100px". E.g "top" or "bottom".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #alignX(String)
+	 * @see #alignY(String)
+	 */
+	public final LC align(String ax, String ay)
+	{
+		if (ax != null)
+			alignX(ax);
+
+		if (ay != null)
+			alignY(ay);
+
+		return this;
+	}
+
+	/** Same functionality as <code>setGridGapX(ConstraintParser.parseBoundSize(boundsSize, true, true))</code> only this method
+	 * returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param boundsSize The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+	 * <code>"50:100:200"</code> or <code>"100px"</code>.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setGridGapX(BoundSize)
+	 */
+	public final LC gridGapX(String boundsSize)
+	{
+		setGridGapX(ConstraintParser.parseBoundSize(boundsSize, true, true));
+		return this;
+	}
+
+	/** Same functionality as <code>setGridGapY(ConstraintParser.parseBoundSize(boundsSize, true, false))</code> only this method
+	 * returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param boundsSize The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+	 * <code>"50:100:200"</code> or <code>"100px"</code>.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setGridGapY(BoundSize)
+	 */
+	public final LC gridGapY(String boundsSize)
+	{
+		setGridGapY(ConstraintParser.parseBoundSize(boundsSize, true, false));
+		return this;
+	}
+
+	/** Sets both grid gaps at the same time. see {@link #gridGapX(String)} and {@link #gridGapY(String)}.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param gapx The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+	 * <code>"50:100:200"</code> or <code>"100px"</code>.
+	 * @param gapy The <code>BoundSize</code> of the gap. This is a minimum and/or preferred and/or maximum size. E.g.
+	 * <code>"50:100:200"</code> or <code>"100px"</code>.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #gridGapX(String)
+	 * @see #gridGapY(String)
+	 */
+	public final LC gridGap(String gapx, String gapy)
+	{
+		if (gapx != null)
+			gridGapX(gapx);
+
+		if (gapy != null)
+			gridGapY(gapy);
+
+		return this;
+	}
+
+	/** Same functionality as {@link #setDebugMillis(int repaintMillis)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param repaintMillis The new debug repaint interval.
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setDebugMillis(int)
+	 */
+	public final LC debug(int repaintMillis)
+	{
+		setDebugMillis(repaintMillis);
+		return this;
+	}
+
+	/** Same functionality as {@link #setHideMode(int mode)} only this method returns <code>this</code> for chaining multiple calls.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcomponents.com.
+	 * @param mode The mode:<br>
+	 * 0 == Normal. Bounds will be calculated as if the component was visible.<br>
+	 * 1 == If hidden the size will be 0, 0 but the gaps remain.<br>
+	 * 2 == If hidden the size will be 0, 0 and gaps set to zero.<br>
+	 * 3 == If hidden the component will be disregarded completely and not take up a cell in the grid..
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 * @see #setHideMode(int)
+	 */
+	public final LC hideMode(int mode)
+	{
+		setHideMode(mode);
+		return this;
+	}
+
+	/** The minimum width for the container. The value will override any value that is set on the container itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+	 * @param width The width expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC minWidth(String width)
+	{
+		setWidth(LayoutUtil.derive(getWidth(), ConstraintParser.parseUnitValue(width, true), null, null));
+		return this;
+	}
+
+	/** The width for the container as a min and/or preferred and/or maximum width. The value will override any value that is set on
+	 * the container itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+	 * @param width The width expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC width(String width)
+	{
+		setWidth(ConstraintParser.parseBoundSize(width, false, true));
+		return this;
+	}
+
+	/** The maximum width for the container. The value will override any value that is set on the container itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+	 * @param width The width expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC maxWidth(String width)
+	{
+		setWidth(LayoutUtil.derive(getWidth(), null, null, ConstraintParser.parseUnitValue(width, true)));
+		return this;
+	}
+
+	/** The minimum height for the container. The value will override any value that is set on the container itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or Cheat Sheet at www.migcontainers.com.
+	 * @param height The height expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC minHeight(String height)
+	{
+		setHeight(LayoutUtil.derive(getHeight(), ConstraintParser.parseUnitValue(height, false), null, null));
+		return this;
+	}
+
+	/** The height for the container as a min and/or preferred and/or maximum height. The value will override any value that is set on
+	 * the container itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcontainers.com.
+	 * @param height The height expressed as a <code>BoundSize</code>. E.g. "50:100px:200mm" or "100px".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC height(String height)
+	{
+		setHeight(ConstraintParser.parseBoundSize(height, false, false));
+		return this;
+	}
+
+	/** The maximum height for the container. The value will override any value that is set on the container itself.
+	 * <p>
+	 * For a more thorough explanation of what this constraint does see the white paper or cheat Sheet at www.migcontainers.com.
+	 * @param height The height expressed as a <code>UnitValue</code>. E.g. "100px" or "200mm".
+	 * @return <code>this</code> so it is possible to chain calls. E.g. <code>new LayoutConstraint().noGrid().gap().fill()</code>.
+	 */
+	public final LC maxHeight(String height)
+	{
+		setHeight(LayoutUtil.derive(getHeight(), null, null, ConstraintParser.parseUnitValue(height, false)));
+		return this;
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+
+	public void writeExternal(ObjectOutput out) throws IOException
+	{
+		if (getClass() == LC.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+}
diff --git a/src/net/miginfocom/layout/LayoutCallback.java b/src/net/miginfocom/layout/LayoutCallback.java
new file mode 100644
index 0000000..2a06b1d
--- /dev/null
+++ b/src/net/miginfocom/layout/LayoutCallback.java
@@ -0,0 +1,77 @@
+package net.miginfocom.layout;
+
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A class to extend if you want to provide more control over where a component is placed or the size of it.
+ * <p>
+ * Note! Returned arrays from this class will never be altered. This means that caching of arrays in these methods
+ * is OK.
+ */
+public abstract class LayoutCallback
+{
+	/** Returns a position similar to the "pos" the component constraint.
+	 * @param comp The component wrapper that holds the actual component (JComponent is Swing and Control in SWT).
+	 * <b>Should not be altered.</b>
+	 * @return The [x, y, x2, y2] as explained in the documentation for "pos". If <code>null</code>
+	 * is returned nothing is done and this is the default.
+	 * @see UnitValue
+	 * @see net.miginfocom.layout.ConstraintParser#parseUnitValue(String, boolean)
+	 */
+	public UnitValue[] getPosition(ComponentWrapper comp)
+	{
+		return null;
+	}
+
+	/** Returns a size similar to the "width" and "height" in the component constraint.
+	 * @param comp The component wrapper that holds the actual component (JComponent is Swing and Control in SWT).
+	 * <b>Should not be altered.</b>
+	 * @return The [width, height] as explained in the documentation for "width" and "height". If <code>null</code>
+	 * is returned nothing is done and this is the default.
+	 * @see net.miginfocom.layout.BoundSize
+	 * @see net.miginfocom.layout.ConstraintParser#parseBoundSize(String, boolean, boolean)
+	 */
+	public BoundSize[] getSize(ComponentWrapper comp)
+	{
+		return null;
+	}
+
+	/** A last minute change of the bounds. The bound for the layout cycle has been set and you can correct there
+	 * after any set of rules you like.
+	 * @param comp The component wrapper that holds the actual component (JComponent is Swing and Control in SWT).
+	 */
+	public void correctBounds(ComponentWrapper comp)
+	{
+	}
+}
diff --git a/src/net/miginfocom/layout/LayoutUtil.java b/src/net/miginfocom/layout/LayoutUtil.java
new file mode 100644
index 0000000..7206e14
--- /dev/null
+++ b/src/net/miginfocom/layout/LayoutUtil.java
@@ -0,0 +1,568 @@
+package net.miginfocom.layout;
+
+import java.beans.*;
+import java.io.*;
+import java.util.IdentityHashMap;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A utility class that has only static helper methods.
+ */
+public final class LayoutUtil
+{
+	/** A substitute value for aa really large value. Integer.MAX_VALUE is not used since that means a lot of defensive code
+	 * for potential overflow must exist in many places. This value is large enough for being unreasonable yet it is hard to
+	 * overflow.
+	 */
+	public static final int INF = (Integer.MAX_VALUE >> 10) - 100; // To reduce likelihood of overflow errors when calculating.
+
+	/** Tag int for a value that in considered "not set". Used as "null" element in int arrays.
+	 */
+	static final int NOT_SET = Integer.MIN_VALUE + 12346;   // Magic value...
+
+	// Index for the different sizes
+	public static final int MIN = 0;
+	public static final int PREF = 1;
+	public static final int MAX = 2;
+
+	private static volatile WeakHashMap<Object, String> CR_MAP = null;
+	private static volatile WeakHashMap<Object, Boolean> DT_MAP = null;      // The Containers that have design time. Value not used.
+	private static int eSz = 0;
+	private static int globalDebugMillis = 0;
+    public static final boolean HAS_BEANS = hasBeans();
+
+    private static boolean hasBeans()
+    {
+        try {
+            LayoutUtil.class.getClassLoader().loadClass("java.beans.Beans");
+            return true;
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+	private LayoutUtil()
+	{
+	}
+
+	/** Returns the current version of MiG Layout.
+	 * @return The current version of MiG Layout. E.g. "3.6.3" or "4.0"
+	 */
+	public static String getVersion()
+	{
+		return "4.0";
+	}
+
+	/** If global debug should be on or off. If > 0 then debug is turned on for all MigLayout
+	 * instances.
+	 * @return The current debug milliseconds.
+	 * @see LC#setDebugMillis(int)
+	 */
+	public static int getGlobalDebugMillis()
+	{
+		return globalDebugMillis;
+	}
+
+	/** If global debug should be on or off. If > 0 then debug is turned on for all MigLayout
+	 * instances.
+	 * <p>
+	 * Note! This is a passive value and will be read by panels when the needed, which is normally
+	 * when they repaint/layout.
+	 * @param millis The new debug milliseconds. 0 turns of global debug and leaves debug up to every
+	 * individual panel.
+	 * @see LC#setDebugMillis(int)
+	 */
+	public static void setGlobalDebugMillis(int millis)
+	{
+		globalDebugMillis = millis;
+	}
+
+	/** Sets if design time is turned on for a Container in {@link ContainerWrapper}.
+	 * @param cw The container to set design time for. <code>null</code> is legal and can be used as
+	 * a key to turn on/off design time "in general". Note though that design time "in general" is
+	 * always on as long as there is at least one ContainerWrapper with design time.
+	 * <p>
+	 * <strong>If this method has not ever been called it will default to what
+	 * <code>Beans.isDesignTime()</code> returns.</strong> This means that if you call
+	 * this method you indicate that you will take responsibility for the design time value.
+	 * @param b <code>true</code> means design time on.
+	 */
+	public static void setDesignTime(ContainerWrapper cw, boolean b)
+	{
+		if (DT_MAP == null)
+			DT_MAP = new WeakHashMap<Object, Boolean>();
+
+		DT_MAP.put((cw != null ? cw.getComponent() : null), b);
+	}
+
+	/** Returns if design time is turned on for a Container in {@link ContainerWrapper}.
+	 * @param cw The container to set design time for. <code>null</code> is legal will return <code>true</code>
+	 * if there is at least one <code>ContainerWrapper</code> (or <code>null</code>) that have design time
+	 * turned on.
+	 * @return If design time is set for <code>cw</code>.
+	 */
+	public static boolean isDesignTime(ContainerWrapper cw)
+	{
+		if (DT_MAP == null)
+			return HAS_BEANS && Beans.isDesignTime();
+
+		if (cw != null && DT_MAP.containsKey(cw.getComponent()) == false)
+			cw = null;
+
+		Boolean b = DT_MAP.get(cw != null ? cw.getComponent() : null);
+		return b != null && b;
+	}
+
+	/** The size of an empty row or columns in a grid during design time.
+	 * @return The number of pixels. Default is 15.
+	 */
+	public static int getDesignTimeEmptySize()
+	{
+		return eSz;
+	}
+
+	/** The size of an empty row or columns in a grid during design time.
+	 * @param pixels The number of pixels. Default is 0 (it was 15 prior to v3.7.2, but since that meant different behaviour
+	 * under design time by default it was changed to be 0, same as non-design time). IDE vendors can still set it to 15 to
+	 * get the old behaviour.
+	 */
+	public static void setDesignTimeEmptySize(int pixels)
+	{
+		eSz = pixels;
+	}
+
+	/** Associates <code>con</code> with the creation string <code>s</code>. The <code>con</code> object should
+	 * probably have an equals method that compares identities or <code>con</code> objects that .equals() will only
+	 * be able to have <b>one</b> creation string.
+	 * <p>
+	 * If {@link LayoutUtil#isDesignTime(ContainerWrapper)} returns <code>false</code> the method does nothing.
+	 * @param con The object. if <code>null</code> the method does nothing.
+	 * @param s The creation string. if <code>null</code> the method does nothing.
+	 */
+	static void putCCString(Object con, String s)
+	{
+		if (s != null && con != null && isDesignTime(null)) {
+			if (CR_MAP == null)
+				CR_MAP = new WeakHashMap<Object, String>(64);
+
+			CR_MAP.put(con, s);
+		}
+	}
+
+	/** Sets/add the persistence delegates to be used for a class.
+	 * @param c The class to set the registered deligate for.
+	 * @param del The new delegate or <code>null</code> to erase to old one.
+	 */
+	@SuppressWarnings("rawtypes")
+	static synchronized void setDelegate(Class c, PersistenceDelegate del)
+	{
+		try {
+			Introspector.getBeanInfo(c, Introspector.IGNORE_ALL_BEANINFO).getBeanDescriptor().setValue("persistenceDelegate", del);
+		} catch (Exception ignored) {
+		}
+	}
+
+	/** Returns strings set with {@link #putCCString(Object, String)} or <code>null</code> if nothing is associated or
+	 * {@link LayoutUtil#isDesignTime(ContainerWrapper)} returns <code>false</code>.
+	 * @param con The constrain object.
+	 * @return The creation string or <code>null</code> if nothing is registered with the <code>con</code> object.
+	 */
+	static String getCCString(Object con)
+	{
+		return CR_MAP != null ? CR_MAP.get(con) : null;
+	}
+
+	static void throwCC()
+	{
+		throw new IllegalStateException("setStoreConstraintData(true) must be set for strings to be saved.");
+	}
+
+	/** Takes a number on min/preferred/max sizes and resize constraints and returns the calculated sizes which sum should add up to <code>bounds</code>. Whether the sum
+	 * will actually equal <code>bounds</code> is dependent om the pref/max sizes and resize constraints.
+	 * @param sizes [ix],[MIN][PREF][MAX]. Grid.CompWrap.NOT_SET will be treated as N/A or 0. A "[MIN][PREF][MAX]" array with null elements will be interpreted as very flexible (no bounds)
+	 * but if the array itself is null it will not get any size.
+	 * @param resConstr Elements can be <code>null</code> and the whole array can be <code>null</code>. <code>null</code> means that the size will not be flexible at all.
+	 * Can have length less than <code>sizes</code> in which case the last element should be used for the elements missing.
+	 * @param defPushWeights If there is no grow weight for a resConstr the corresponding value of this array is used.
+	 * These forced resConstr will be grown last though and only if needed to fill to the bounds.
+	 * @param startSizeType The initial size to use. E.g. {@link net.miginfocom.layout.LayoutUtil#MIN}.
+	 * @param bounds To use for relative sizes.
+	 * @return The sizes. Array length will match <code>sizes</code>.
+	 */
+	static int[] calculateSerial(int[][] sizes, ResizeConstraint[] resConstr, Float[] defPushWeights, int startSizeType, int bounds)
+	{
+		float[] lengths = new float[sizes.length];	// heights/widths that are set
+		float usedLength = 0.0f;
+
+		// Give all preferred size to start with
+		for (int i = 0; i < sizes.length; i++) {
+			if (sizes[i] != null) {
+				float len = sizes[i][startSizeType] != NOT_SET ? sizes[i][startSizeType] : 0;
+				int newSizeBounded = getBrokenBoundary(len, sizes[i][MIN], sizes[i][MAX]);
+				if (newSizeBounded != NOT_SET)
+					len = newSizeBounded;
+
+				usedLength += len;
+				lengths[i] = len;
+			}
+		}
+
+		int useLengthI = Math.round(usedLength);
+		if (useLengthI != bounds && resConstr != null) {
+			boolean isGrow = useLengthI < bounds;
+
+			// Create a Set with the available priorities
+			TreeSet<Integer> prioList = new TreeSet<Integer>();
+			for (int i = 0; i < sizes.length; i++) {
+				ResizeConstraint resC = (ResizeConstraint) getIndexSafe(resConstr, i);
+				if (resC != null)
+					prioList.add(isGrow ? resC.growPrio : resC.shrinkPrio);
+			}
+			Integer[] prioIntegers = prioList.toArray(new Integer[prioList.size()]);
+
+			for (int force = 0; force <= ((isGrow && defPushWeights != null) ? 1 : 0); force++) {    // Run twice if defGrow and the need for growing.
+				for (int pr = prioIntegers.length - 1; pr >= 0; pr--) {
+					int curPrio = prioIntegers[pr];
+
+					float totWeight = 0f;
+					Float[] resizeWeight = new Float[sizes.length];
+					for (int i = 0; i < sizes.length; i++) {
+						if (sizes[i] == null)   // if no min/pref/max size at all do not grow or shrink.
+							continue;
+
+						ResizeConstraint resC = (ResizeConstraint) getIndexSafe(resConstr, i);
+						if (resC != null) {
+							int prio = isGrow ? resC.growPrio : resC.shrinkPrio;
+
+							if (curPrio == prio) {
+								if (isGrow) {
+									resizeWeight[i] = (force == 0 || resC.grow != null) ? resC.grow : (defPushWeights[i < defPushWeights.length ? i : defPushWeights.length - 1]);
+								} else {
+									resizeWeight[i] = resC.shrink;
+								}
+								if (resizeWeight[i] != null)
+									totWeight += resizeWeight[i];
+							}
+						}
+					}
+
+					if (totWeight > 0f) {
+						boolean hit;
+						do {
+							float toChange = bounds - usedLength;
+							hit = false;
+							float changedWeight = 0f;
+							for (int i = 0; i < sizes.length && totWeight > 0.0001f; i++) {
+
+								Float weight = resizeWeight[i];
+								if (weight != null) {
+									float sizeDelta = toChange * weight / totWeight;
+									float newSize = lengths[i] + sizeDelta;
+
+									if (sizes[i] != null) {
+										int newSizeBounded = getBrokenBoundary(newSize, sizes[i][MIN], sizes[i][MAX]);
+										if (newSizeBounded != NOT_SET) {
+											resizeWeight[i] = null;
+											hit = true;
+											changedWeight += weight;
+											newSize = newSizeBounded;
+											sizeDelta = newSize - lengths[i];
+										}
+									}
+
+									lengths[i] = newSize;
+									usedLength += sizeDelta;
+								}
+							}
+							totWeight -= changedWeight;
+						} while (hit);
+					}
+				}
+			}
+		}
+		return roundSizes(lengths);
+	}
+
+	static Object getIndexSafe(Object[] arr, int ix)
+	{
+		return arr != null ? arr[ix < arr.length ? ix : arr.length - 1] : null;
+	}
+
+	/** Returns the broken boundary if <code>sz</code> is outside the boundaries <code>lower</code> or <code>upper</code>. If both boundaries
+	 * are broken, the lower one is returned. If <code>sz</code> is < 0 then <code>new Float(0f)</code> is returned so that no sizes can be
+	 * negative.
+	 * @param sz The size to check
+	 * @param lower The lower boundary (or <code>null</code> fo no boundary).
+	 * @param upper The upper boundary (or <code>null</code> fo no boundary).
+	 * @return The broken boundary.
+	 */
+	private static int getBrokenBoundary(float sz, int lower, int upper)
+	{
+		if (lower != NOT_SET) {
+			if (sz < lower)
+				return lower;
+		} else if (sz < 0f) {
+			return 0;
+		}
+
+		if (upper != NOT_SET && sz > upper)
+			return upper;
+
+		return NOT_SET;
+	}
+
+
+	static int sum(int[] terms, int start, int len)
+	{
+		int s = 0;
+		for (int i = start, iSz = start + len; i < iSz; i++)
+			s += terms[i];
+		return s;
+	}
+
+	static int sum(int[] terms)
+	{
+		return sum(terms, 0, terms.length);
+	}
+
+	public static int getSizeSafe(int[] sizes, int sizeType)
+	{
+		if (sizes == null || sizes[sizeType] == NOT_SET)
+			return sizeType == MAX ? LayoutUtil.INF : 0;
+		return sizes[sizeType];
+	}
+
+	static BoundSize derive(BoundSize bs, UnitValue min, UnitValue pref, UnitValue max)
+	{
+		if (bs == null || bs.isUnset())
+			return new BoundSize(min, pref, max, null);
+
+		return new BoundSize(
+				min != null ? min : bs.getMin(),
+				pref != null ? pref : bs.getPreferred(),
+				max != null ? max : bs.getMax(),
+				bs.getGapPush(),
+				null);
+	}
+
+	/** Returns if left-to-right orientation is used. If not set explicitly in the layout constraints the Locale
+	 * of the <code>parent</code> is used.
+	 * @param lc The constraint if there is one. Can be <code>null</code>.
+	 * @param container The parent that may be used to get the left-to-right if ffc does not specify this.
+	 * @return If left-to-right orientation is currently used.
+	 */
+	public static boolean isLeftToRight(LC lc, ContainerWrapper container)
+	{
+		if (lc != null && lc.getLeftToRight() != null)
+			return lc.getLeftToRight();
+
+		return container == null || container.isLeftToRight();
+	}
+
+	/** Round a number of float sizes into int sizes so that the total length match up
+	 * @param sizes The sizes to round
+	 * @return An array of equal length as <code>sizes</code>.
+	 */
+	static int[] roundSizes(float[] sizes)
+	{
+		int[] retInts = new int[sizes.length];
+		float posD = 0;
+
+		for (int i = 0; i < retInts.length; i++) {
+			int posI = (int) (posD + 0.5f);
+
+			posD += sizes[i];
+
+			retInts[i] = (int) (posD + 0.5f) - posI;
+		}
+
+		return retInts;
+	}
+
+	/** Safe equals. null == null, but null never equals anything else.
+	 * @param o1 The first object. May be <code>null</code>.
+	 * @param o2 The second object. May be <code>null</code>.
+	 * @return Returns <code>true</code> if <code>o1</code> and <code>o2</code> are equal (using .equals()) or both are <code>null</code>.
+	 */
+	static boolean equals(Object o1, Object o2)
+	{
+		return o1 == o2 || (o1 != null && o2 != null && o1.equals(o2));
+	}
+
+//	static int getBaselineCorrect(Component comp)
+//	{
+//		Dimension pSize = comp.getPreferredSize();
+//		int baseline = comp.getBaseline(pSize.width, pSize.height);
+//		int nextBaseline = comp.getBaseline(pSize.width, pSize.height + 1);
+//
+//		// Amount to add to height when calculating where baseline
+//		// lands for a particular height:
+//		int padding = 0;
+//
+//		// Where the baseline is relative to the mid point
+//		int baselineOffset = baseline - pSize.height / 2;
+//		if (pSize.height % 2 == 0 && baseline != nextBaseline) {
+//			padding = 1;
+//		} else if (pSize.height % 2 == 1 && baseline == nextBaseline) {
+//			baselineOffset--;
+//			padding = 1;
+//		}
+//
+//		// The following calculates where the baseline lands for
+//		// the height z:
+//		return (pSize.height + padding) / 2 + baselineOffset;
+//	}
+
+
+	/** Returns the inset for the side.
+	 * @param side top == 0, left == 1, bottom = 2, right = 3.
+	 * @param getDefault If <code>true</code> the default insets will get retrieved if <code>lc</code> has none set.
+	 * @return The inset for the side. Never <code>null</code>.
+	 */
+	static UnitValue getInsets(LC lc, int side, boolean getDefault)
+	{
+		UnitValue[] i = lc.getInsets();
+		return (i != null && i[side] != null) ? i[side] : (getDefault ? PlatformDefaults.getPanelInsets(side) : UnitValue.ZERO);
+	}
+
+	/** Writes the objet and CLOSES the stream. Uses the persistence delegate registered in this class.
+	 * @param os The stream to write to. Will be closed.
+	 * @param o The object to be serialized.
+	 * @param listener The listener to recieve the exeptions if there are any. If <code>null</code> not used.
+	 */
+	static void writeXMLObject(OutputStream os, Object o, ExceptionListener listener)
+	{
+		ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(LayoutUtil.class.getClassLoader());
+
+		XMLEncoder encoder = new XMLEncoder(os);
+
+		if (listener != null)
+			encoder.setExceptionListener(listener);
+
+		encoder.writeObject(o);
+        encoder.close();    // Must be closed to write.
+
+		Thread.currentThread().setContextClassLoader(oldClassLoader);
+	}
+
+	private static ByteArrayOutputStream writeOutputStream = null;
+	/** Writes an object to XML.
+	 * @param out The boject out to write to. Will not be closed.
+	 * @param o The object to write.
+	 */
+	public static synchronized void writeAsXML(ObjectOutput out, Object o) throws IOException
+	{
+		if (writeOutputStream == null)
+			writeOutputStream = new ByteArrayOutputStream(16384);
+
+		writeOutputStream.reset();
+
+		writeXMLObject(writeOutputStream, o, new ExceptionListener() {
+			public void exceptionThrown(Exception e) {
+				e.printStackTrace();
+			}});
+
+		byte[] buf = writeOutputStream.toByteArray();
+
+		out.writeInt(buf.length);
+		out.write(buf);
+	}
+
+	private static byte[] readBuf = null;
+	/** Reads an object from <code>in</code> using the
+	 * @param in The object input to read from.
+	 * @return The object. Never <code>null</code>.
+	 * @throws IOException If there was a problem saving as XML
+	 */
+	@SuppressWarnings("resource")
+	public static synchronized Object readAsXML(ObjectInput in) throws IOException
+	{
+		if (readBuf == null)
+			readBuf = new byte[16384];
+
+		Thread cThread = Thread.currentThread();
+		ClassLoader oldCL = null;
+
+		try {
+			oldCL = cThread.getContextClassLoader();
+			cThread.setContextClassLoader(LayoutUtil.class.getClassLoader());
+		} catch(SecurityException ignored) {
+		}
+
+		Object o = null;
+		try {
+			int length = in.readInt();
+			if (length > readBuf.length)
+				readBuf = new byte[length];
+
+			in.readFully(readBuf, 0, length);
+
+			o = new XMLDecoder(new ByteArrayInputStream(readBuf, 0, length)).readObject();
+
+		} catch(EOFException ignored) {
+		}
+
+		if (oldCL != null)
+			cThread.setContextClassLoader(oldCL);
+
+		return o;
+	}
+
+	private static final IdentityHashMap<Object, Object> SER_MAP = new IdentityHashMap<Object, Object>(2);
+
+	/** Sets the serialized object and associates it with <code>caller</code>.
+	 * @param caller The object created <code>o</code>
+	 * @param o The just serialized object.
+	 */
+	public static void setSerializedObject(Object caller, Object o)
+	{
+		synchronized(SER_MAP) {
+			SER_MAP.put(caller, o);
+		}
+	}
+
+	/** Returns the serialized object that are associated with <code>caller</code>. It also removes it from the list.
+	 * @param caller The original creator of the object.
+	 * @return The object.
+	 */
+	public static Object getSerializedObject(Object caller)
+	{
+		synchronized(SER_MAP) {
+			return SER_MAP.remove(caller);
+		}
+	}
+}
diff --git a/src/net/miginfocom/layout/LinkHandler.java b/src/net/miginfocom/layout/LinkHandler.java
new file mode 100644
index 0000000..45ad266
--- /dev/null
+++ b/src/net/miginfocom/layout/LinkHandler.java
@@ -0,0 +1,200 @@
+package net.miginfocom.layout;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/**
+ */
+public final class LinkHandler
+{
+	public static final int X = 0;
+	public static final int Y = 1;
+	public static final int WIDTH = 2;
+	public static final int HEIGHT = 3;
+	public static final int X2 = 4;
+	public static final int Y2 = 5;
+
+	private static final ArrayList<WeakReference<Object>> LAYOUTS = new ArrayList<WeakReference<Object>>(4);
+	private static final ArrayList<HashMap<String, int[]>> VALUES = new ArrayList<HashMap<String, int[]>>(4);
+	private static final ArrayList<HashMap<String, int[]>> VALUES_TEMP = new ArrayList<HashMap<String, int[]>>(4);
+
+	private LinkHandler()
+	{
+	}
+
+	public synchronized static Integer getValue(Object layout, String key, int type)
+	{
+		Integer ret = null;
+		boolean cont = true;
+
+		for (int i = LAYOUTS.size() - 1; i >= 0; i--) {
+			Object l = LAYOUTS.get(i).get();
+			if (ret == null && l == layout) {
+				int[] rect = VALUES_TEMP.get(i).get(key);
+				if (cont && rect != null && rect[type] != LayoutUtil.NOT_SET) {
+					ret = rect[type];
+				} else {
+					rect = VALUES.get(i).get(key);
+					ret = (rect != null && rect[type] != LayoutUtil.NOT_SET) ? rect[type] : null;
+				}
+				cont = false;
+			}
+
+			if (l == null) {
+				LAYOUTS.remove(i);
+				VALUES.remove(i);
+				VALUES_TEMP.remove(i);
+			}
+		}
+		return ret;
+	}
+
+	/** Sets a key that can be linked to from any component.
+	 * @param layout The MigLayout instance
+	 * @param key The key to link to. This is the same as the ID in a component constraint.
+	 * @param x x
+	 * @param y y
+	 * @param width Width
+	 * @param height Height
+	 * @return If the value was changed
+	 */
+	public synchronized static boolean setBounds(Object layout, String key, int x, int y, int width, int height)
+	{
+		return setBounds(layout, key, x, y, width, height, false, false);
+	}
+
+	synchronized static boolean setBounds(Object layout, String key, int x, int y, int width, int height, boolean temporary, boolean incCur)
+	{
+		for (int i = LAYOUTS.size() - 1; i >= 0; i--) {
+			Object l = LAYOUTS.get(i).get();
+			if (l == layout) {
+				HashMap<String, int[]> map = (temporary ? VALUES_TEMP : VALUES).get(i);
+				int[] old = map.get(key);
+
+				if (old == null || old[X] != x || old[Y] != y || old[WIDTH] != width || old[HEIGHT] != height) {
+					if (old == null || incCur == false) {
+						map.put(key, new int[] {x, y, width, height, x + width, y + height});
+						return true;
+					} else {
+						boolean changed = false;
+
+						if (x != LayoutUtil.NOT_SET) {
+							if (old[X] == LayoutUtil.NOT_SET || x < old[X]) {
+								old[X] = x;
+								old[WIDTH] = old[X2] - x;
+								changed = true;
+							}
+
+							if (width != LayoutUtil.NOT_SET) {
+								int x2 = x + width;
+								if (old[X2] == LayoutUtil.NOT_SET || x2 > old[X2]) {
+									old[X2] = x2;
+									old[WIDTH] = x2 - old[X];
+									changed = true;
+								}
+							}
+						}
+
+						if (y != LayoutUtil.NOT_SET) {
+							if (old[Y] == LayoutUtil.NOT_SET || y < old[Y]) {
+								old[Y] = y;
+								old[HEIGHT] = old[Y2] - y;
+								changed = true;
+							}
+
+							if (height != LayoutUtil.NOT_SET) {
+								int y2 = y + height;
+								if (old[Y2] == LayoutUtil.NOT_SET || y2 > old[Y2]) {
+									old[Y2] = y2;
+									old[HEIGHT] = y2 - old[Y];
+									changed = true;
+								}
+							}
+						}
+						return changed;
+					}
+				}
+				return false;
+			}
+		}
+
+		LAYOUTS.add(new WeakReference<Object>(layout));
+		int[] bounds = new int[] {x, y, width, height, x + width, y + height};
+
+		HashMap<String, int[]> values = new HashMap<String, int[]>(4);
+		if (temporary)
+			values.put(key, bounds);
+		VALUES_TEMP.add(values);
+
+		values = new HashMap<String, int[]>(4);
+		if (temporary == false)
+			values.put(key, bounds);
+		VALUES.add(values);
+
+		return true;
+	}
+
+	/** This method clear any weak references right away instead of waiting for the GC. This might be advantageous
+	 * if lots of layout are created and disposed of quickly to keep memory consumption down.
+	 * @since 3.7.4
+	 */
+	public synchronized static void clearWeakReferencesNow()
+	{
+		LAYOUTS.clear();
+	}
+
+	public synchronized static boolean clearBounds(Object layout, String key)
+	{
+		for (int i = LAYOUTS.size() - 1; i >= 0; i--) {
+			Object l = LAYOUTS.get(i).get();
+			if (l == layout)
+				return VALUES.get(i).remove(key) != null;
+		}
+		return false;
+	}
+
+	synchronized static void clearTemporaryBounds(Object layout)
+	{
+		for (int i = LAYOUTS.size() - 1; i >= 0; i--) {
+			Object l = LAYOUTS.get(i).get();
+			if (l == layout) {
+				VALUES_TEMP.get(i).clear();
+				return;
+			}
+		}
+	}
+}
diff --git a/src/net/miginfocom/layout/PlatformDefaults.java b/src/net/miginfocom/layout/PlatformDefaults.java
new file mode 100644
index 0000000..1a6e158
--- /dev/null
+++ b/src/net/miginfocom/layout/PlatformDefaults.java
@@ -0,0 +1,772 @@
+package net.miginfocom.layout;
+
+import javax.swing.*;
+import java.util.HashMap;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ * @author Xxxx Xxxx, Xxxx  - Gnome support
+ *         Date: 2008-jan-16
+ */
+
+/** Currently handles Windows, Mac OS X, and GNOME spacing.
+ */
+public final class PlatformDefaults
+{
+	private static int DEF_H_UNIT = UnitValue.LPX;
+	private static int DEF_V_UNIT = UnitValue.LPY;
+
+	private static InCellGapProvider GAP_PROVIDER = null;
+
+	private static volatile int MOD_COUNT = 0;
+
+	private static final UnitValue LPX4 = new UnitValue(4, UnitValue.LPX, null);
+	private static final UnitValue LPX6 = new UnitValue(6, UnitValue.LPX, null);
+	private static final UnitValue LPX7 = new UnitValue(7, UnitValue.LPX, null);
+//	private static final UnitValue LPX8 = new UnitValue(8, UnitValue.LPX, null);
+	private static final UnitValue LPX9 = new UnitValue(9, UnitValue.LPX, null);
+	private static final UnitValue LPX10 = new UnitValue(10, UnitValue.LPX, null);
+	private static final UnitValue LPX11 = new UnitValue(11, UnitValue.LPX, null);
+	private static final UnitValue LPX12 = new UnitValue(12, UnitValue.LPX, null);
+	private static final UnitValue LPX14 = new UnitValue(14, UnitValue.LPX, null);
+	private static final UnitValue LPX16 = new UnitValue(16, UnitValue.LPX, null);
+	private static final UnitValue LPX18 = new UnitValue(18, UnitValue.LPX, null);
+	private static final UnitValue LPX20 = new UnitValue(20, UnitValue.LPX, null);
+
+	private static final UnitValue LPY4 = new UnitValue(4, UnitValue.LPY, null);
+	private static final UnitValue LPY6 = new UnitValue(6, UnitValue.LPY, null);
+	private static final UnitValue LPY7 = new UnitValue(7, UnitValue.LPY, null);
+//	private static final UnitValue LPY8 = new UnitValue(8, UnitValue.LPY, null);
+	private static final UnitValue LPY9 = new UnitValue(9, UnitValue.LPY, null);
+	private static final UnitValue LPY10 = new UnitValue(10, UnitValue.LPY, null);
+	private static final UnitValue LPY11 = new UnitValue(11, UnitValue.LPY, null);
+	private static final UnitValue LPY12 = new UnitValue(12, UnitValue.LPY, null);
+	private static final UnitValue LPY14 = new UnitValue(14, UnitValue.LPY, null);
+	private static final UnitValue LPY16 = new UnitValue(16, UnitValue.LPY, null);
+	private static final UnitValue LPY18 = new UnitValue(18, UnitValue.LPY, null);
+	private static final UnitValue LPY20 = new UnitValue(20, UnitValue.LPY, null);
+
+	public static final int WINDOWS_XP = 0;
+	public static final int MAC_OSX = 1;
+	public static final int GNOME = 2;
+//	private static final int KDE = 3;
+
+	private static int CUR_PLAF = WINDOWS_XP;
+
+	// Used for holding values.
+	private final static UnitValue[] PANEL_INS = new UnitValue[4];
+	private final static UnitValue[] DIALOG_INS = new UnitValue[4];
+
+	private static String BUTTON_FORMAT = null;
+
+	private static final HashMap<String, UnitValue> HOR_DEFS = new HashMap<String, UnitValue>(32);
+	private static final HashMap<String, UnitValue> VER_DEFS = new HashMap<String, UnitValue>(32);
+	private static BoundSize DEF_VGAP = null, DEF_HGAP = null;
+	static BoundSize RELATED_X = null, RELATED_Y = null, UNRELATED_X = null, UNRELATED_Y = null;
+	private static UnitValue BUTT_WIDTH = null;
+
+	private static Float horScale = null, verScale = null;
+
+	/** I value indicating that the size of the font for the container of the component
+	 * will be used as a base for calculating the logical pixel size. This is much as how
+	 * Windows calculated DLU (dialog units).
+	 * @see net.miginfocom.layout.UnitValue#LPX
+	 * @see net.miginfocom.layout.UnitValue#LPY
+	 * @see #setLogicalPixelBase(int)
+	 */
+	public static final int BASE_FONT_SIZE = 100;
+
+	/** I value indicating that the screen DPI will be used as a base for calculating the
+	 * logical pixel size.
+	 * <p>
+	 * This is the default value.
+	 * @see net.miginfocom.layout.UnitValue#LPX
+	 * @see net.miginfocom.layout.UnitValue#LPY
+	 * @see #setLogicalPixelBase(int)
+	 * @see #setVerticalScaleFactor(Float)
+	 * @see #setHorizontalScaleFactor(Float)
+	 */
+	public static final int BASE_SCALE_FACTOR = 101;
+
+	/** I value indicating that the size of a logical pixel should always be a real pixel
+	 * and thus no compensation will be made.
+	 * @see net.miginfocom.layout.UnitValue#LPX
+	 * @see net.miginfocom.layout.UnitValue#LPY
+	 * @see #setLogicalPixelBase(int)
+	 */
+	public static final int BASE_REAL_PIXEL = 102;
+
+	private static int LP_BASE = BASE_SCALE_FACTOR;
+
+	private static Integer BASE_DPI_FORCED = null;
+	private static int BASE_DPI = 96;
+
+	private static boolean dra = true;
+
+	static {
+		setPlatform(getCurrentPlatform());
+		MOD_COUNT = 0;
+	}
+
+	/** Returns the platform that the JRE is running on currently.
+	 * @return The platform that the JRE is running on currently. E.g. {@link #MAC_OSX}, {@link #WINDOWS_XP}, or {@link #GNOME}.
+	 */
+	public static int getCurrentPlatform()
+	{
+		final String os = System.getProperty("os.name");
+		if (os.startsWith("Mac OS")) {
+			return MAC_OSX;
+		} else if (os.startsWith("Linux")) {
+			return GNOME;
+		} else {
+			return WINDOWS_XP;
+		}
+	}
+
+	private PlatformDefaults()
+	{
+	}
+
+	/** Set the defaults to the default for the platform
+	 * @param plaf The platform. <code>PlatformDefaults.WINDOWS_XP</code>,
+	 * <code>PlatformDefaults.MAC_OSX</code>, or
+	 * <code>PlatformDefaults.GNOME</code>.
+	 */
+	public static void setPlatform(int plaf)
+	{
+		switch (plaf) {
+			case WINDOWS_XP:
+				setRelatedGap(LPX4, LPY4);
+				setUnrelatedGap(LPX7, LPY9);
+				setParagraphGap(LPX14, LPY14);
+				setIndentGap(LPX9, LPY9);
+				setGridCellGap(LPX4, LPY4);
+
+				setMinimumButtonWidth(new UnitValue(75, UnitValue.LPX, null));
+				setButtonOrder("L_E+U+YNBXOCAH_R");
+				setDialogInsets(LPY11, LPX11, LPY11, LPX11);
+				setPanelInsets(LPY7, LPX7, LPY7, LPX7);
+				break;
+			case MAC_OSX:
+				setRelatedGap(LPX4, LPY4);
+				setUnrelatedGap(LPX7, LPY9);
+				setParagraphGap(LPX14, LPY14);
+				setIndentGap(LPX10, LPY10);
+				setGridCellGap(LPX4, LPY4);
+
+				setMinimumButtonWidth(new UnitValue(68, UnitValue.LPX, null));
+				setButtonOrder("L_HE+U+NYBXCOA_R");
+				setDialogInsets(LPY14, LPX20, LPY20, LPX20);
+				setPanelInsets(LPY16, LPX16, LPY16, LPX16);
+
+//				setRelatedGap(LPX8, LPY8);
+//				setUnrelatedGap(LPX12, LPY12);
+//				setParagraphGap(LPX16, LPY16);
+//				setIndentGap(LPX10, LPY10);
+//				setGridCellGap(LPX8, LPY8);
+//
+//				setMinimumButtonWidth(new UnitValue(68, UnitValue.LPX, null));
+//				setButtonOrder("L_HE+U+NYBXCOA_R");
+//				setDialogInsets(LPY14, LPX20, LPY20, LPX20);
+//				setPanelInsets(LPY16, LPX16, LPY16, LPX16);
+				break;
+			case GNOME:
+				setRelatedGap(LPX6, LPY6);                    // GNOME HIG 8.2.3
+				setUnrelatedGap(LPX12, LPY12);                // GNOME HIG 8.2.3
+				setParagraphGap(LPX18, LPY18);                // GNOME HIG 8.2.3
+				setIndentGap(LPX12, LPY12);                   // GNOME HIG 8.2.3
+				setGridCellGap(LPX6, LPY6);                   // GNOME HIG 8.2.3
+
+				// GtkButtonBox, child-min-width property default value
+				setMinimumButtonWidth(new UnitValue(85, UnitValue.LPX, null));
+				setButtonOrder("L_HE+UNYACBXIO_R");           // GNOME HIG 3.4.2, 3.7.1
+				setDialogInsets(LPY12, LPX12, LPY12, LPX12);  // GNOME HIG 3.4.3
+				setPanelInsets(LPY6, LPX6, LPY6, LPX6);       // ???
+				break;
+			default:
+				throw new IllegalArgumentException("Unknown platform: " + plaf);
+		}
+		CUR_PLAF = plaf;
+		BASE_DPI = BASE_DPI_FORCED != null ? BASE_DPI_FORCED : getPlatformDPI(plaf);
+	}
+
+	private static int getPlatformDPI(int plaf)
+	{
+		switch (plaf) {
+			case WINDOWS_XP:
+			case GNOME:
+				return 96;
+			case MAC_OSX:
+				try {
+					return System.getProperty("java.version").compareTo("1.6") < 0 ? 72 : 96; // Default DPI was 72 prior to JSE 1.6
+				} catch (Throwable t) {
+					return 72;
+				}
+			default:
+				throw new IllegalArgumentException("Unknown platform: " + plaf);
+		}
+	}
+
+	/** Returns the current platform
+	 * @return <code>PlatformDefaults.WINDOWS</code> or <code>PlatformDefaults.MAC_OSX</code>
+	 */
+	public static int getPlatform()
+	{
+		return CUR_PLAF;
+	}
+
+	public static int getDefaultDPI()
+	{
+		return BASE_DPI;
+	}
+
+	/** Sets the default platform DPI. Normally this is set in the {@link #setPlatform(int)} for the different platforms
+	 * but it can be tweaked here. For instance SWT on Mac does this.
+	 * <p>
+	 * Note that this is not the actual current DPI, but the base DPI for the toolkit.
+	 * @param dpi The base DPI. If null the default DPI is reset to the platform base DPI.
+	 */
+	public static void setDefaultDPI(Integer dpi)
+	{
+		BASE_DPI = dpi != null ? dpi : getPlatformDPI(CUR_PLAF);
+		BASE_DPI_FORCED = dpi;
+	}
+
+	/** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+	 * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+	 * (72 DPI for Mac and 92 DPI for Windows).
+	 * @return The forced scale or <code>null</code> for default scaling.
+	 * @see #getHorizontalScaleFactor()
+	 * @see ComponentWrapper#getHorizontalScreenDPI()
+	 */
+	public static Float getHorizontalScaleFactor()
+	{
+		return horScale;
+	}
+
+	/** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+	 * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+	 * (72 DPI for Mac and 92 DPI for Windows).
+	 * @param f The forced scale or <code>null</code> for default scaling.
+	 * @see #getHorizontalScaleFactor()
+	 * @see ComponentWrapper#getHorizontalScreenDPI()
+	 */
+	public static void setHorizontalScaleFactor(Float f)
+	{
+		if (LayoutUtil.equals(horScale, f) == false) {
+			horScale = f;
+			MOD_COUNT++;
+		}
+	}
+
+	/** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+	 * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+	 * (72 DPI for Mac and 92 DPI for Windows).
+	 * @return The forced scale or <code>null</code> for default scaling.
+	 * @see #getHorizontalScaleFactor()
+	 * @see ComponentWrapper#getVerticalScreenDPI()
+	 */
+	public static Float getVerticalScaleFactor()
+	{
+		return verScale;
+	}
+
+	/** The forced scale factor that all screen relative units (e.g. millimeters, inches and logical pixels) will be multiplied
+	 * with. If <code>null</code> this will default to a scale that will scale the current screen to the default screen resolution
+	 * (72 DPI for Mac and 92 DPI for Windows).
+	 * @param f The forced scale or <code>null</code> for default scaling.
+	 * @see #getHorizontalScaleFactor()
+	 * @see ComponentWrapper#getVerticalScreenDPI()
+	 */
+	public static void setVerticalScaleFactor(Float f)
+	{
+		if (LayoutUtil.equals(verScale, f) == false) {
+			verScale = f;
+			MOD_COUNT++;
+		}
+	}
+
+	/** What base value should be used to calculate logical pixel sizes.
+	 * @return The current base. Default is {@link #BASE_SCALE_FACTOR}
+	 * @see #BASE_FONT_SIZE
+	 * @see #BASE_SCALE_FACTOR
+	 * @see #BASE_REAL_PIXEL
+*/
+	public static int getLogicalPixelBase()
+	{
+		return LP_BASE;
+	}
+
+	/** What base value should be used to calculate logical pixel sizes.
+	 * @param base The new base. Default is {@link #BASE_SCALE_FACTOR}
+	 * @see #BASE_FONT_SIZE
+	 * @see #BASE_SCALE_FACTOR
+	 * @see #BASE_REAL_PIXEL
+	 */
+	public static void setLogicalPixelBase(int base)
+	{
+		if (LP_BASE != base) {
+			if (base < BASE_FONT_SIZE || base > BASE_SCALE_FACTOR)
+				throw new IllegalArgumentException("Unrecognized base: " + base);
+
+			LP_BASE = base;
+			MOD_COUNT++;
+		}
+	}
+
+	/** Sets gap value for components that are "related".
+	 * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 */
+	public static void setRelatedGap(UnitValue x, UnitValue y)
+	{
+		setUnitValue(new String[] {"r", "rel", "related"}, x, y);
+
+		RELATED_X = new BoundSize(x, x, null, "rel:rel");
+		RELATED_Y = new BoundSize(y, y, null, "rel:rel");
+	}
+
+	/** Sets gap value for components that are "unrelated".
+	 * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 */
+	public static void setUnrelatedGap(UnitValue x, UnitValue y)
+	{
+		setUnitValue(new String[] {"u", "unrel", "unrelated"}, x, y);
+
+		UNRELATED_X = new BoundSize(x, x, null, "unrel:unrel");
+		UNRELATED_Y = new BoundSize(y, y, null, "unrel:unrel");
+	}
+
+	/** Sets paragraph gap value for components.
+	 * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 */
+	public static void setParagraphGap(UnitValue x, UnitValue y)
+	{
+		setUnitValue(new String[] {"p", "para", "paragraph"}, x, y);
+	}
+
+	/** Sets gap value for components that are "intended".
+	 * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 */
+	public static void setIndentGap(UnitValue x, UnitValue y)
+	{
+		setUnitValue(new String[] {"i", "ind", "indent"}, x, y);
+	}
+
+	/** Sets gap between two cells in the grid. Note that this is not a gap between component IN a cell, that has to be set
+	 * on the component constraints. The value will be the min and preferred size of the gap.
+	 * @param x The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 * @param y The value that will be transformed to pixels. If <code>null</code> the current value will not change.
+	 */
+	public static void setGridCellGap(UnitValue x, UnitValue y)
+	{
+		if (x != null)
+			DEF_HGAP = new BoundSize(x, x, null, null);
+
+		if (y != null)
+			DEF_VGAP = new BoundSize(y, y, null, null);
+
+		MOD_COUNT++;
+	}
+
+	/** Sets the recommended minimum button width.
+	 * @param width The recommended minimum button width.
+	 */
+	public static void setMinimumButtonWidth(UnitValue width)
+	{
+		BUTT_WIDTH = width;
+		MOD_COUNT++;
+	}
+
+	/** Returns the recommended minimum button width depending on the current set platform.
+	 * @return The recommended minimum button width depending on the current set platform.
+	 */
+	public static UnitValue getMinimumButtonWidth()
+	{
+		return BUTT_WIDTH;
+	}
+
+	/** Returns the unit value associated with the unit. (E.i. "related" or "indent"). Must be lower case.
+	 * @param unit The unit string.
+	 * @return The unit value associated with the unit. <code>null</code> for unrecognized units.
+	 */
+	public static UnitValue getUnitValueX(String unit)
+	{
+		return HOR_DEFS.get(unit);
+	}
+
+	/** Returns the unit value associated with the unit. (E.i. "related" or "indent"). Must be lower case.
+	 * @param unit The unit string.
+	 * @return The unit value associated with the unit. <code>null</code> for unrecognized units.
+	 */
+	public static UnitValue getUnitValueY(String unit)
+	{
+		return VER_DEFS.get(unit);
+	}
+
+	/** Sets the unit value associated with a unit string. This may be used to store values for new unit strings
+	 * or modify old. Note that if a built in unit (such as "related") is modified all versions of it must be
+	 * set (I.e. "r", "rel" and "related"). The build in values will be reset to the default ones if the platform
+	 * is re-set.
+	 * @param unitStrings The unit strings. E.g. "mu", "myunit". Will be converted to lower case and trimmed. Not <code>null</code>.
+	 * @param x The value for the horizontal dimension. If <code>null</code> the value is not changed.
+	 * @param y The value for the vertical dimension. Might be same object as for <code>x</code>. If <code>null</code> the value is not changed.
+	 */
+	public static final void setUnitValue(String[] unitStrings, UnitValue x, UnitValue y)
+	{
+		for (String unitString : unitStrings) {
+			String s = unitString.toLowerCase().trim();
+			if (x != null)
+				HOR_DEFS.put(s, x);
+			if (y != null)
+				VER_DEFS.put(s, y);
+		}
+		MOD_COUNT++;
+	}
+
+	/** Understands ("r", "rel", "related") OR ("u", "unrel", "unrelated") OR ("i", "ind", "indent") OR ("p", "para", "paragraph").
+	 */
+	static int convertToPixels(float value, String unit, boolean isHor, float ref, ContainerWrapper parent, ComponentWrapper comp)
+	{
+		UnitValue uv = (isHor ? HOR_DEFS : VER_DEFS).get(unit);
+		return uv != null ? Math.round(value * uv.getPixels(ref, parent, comp)) : UnitConverter.UNABLE;
+	}
+
+	/** Returns the order for the typical buttons in a standard button bar. It is one letter per button type.
+	 * @return The button order.
+	 * @see #setButtonOrder(String)
+	 */
+	public static String getButtonOrder()
+	{
+		return BUTTON_FORMAT;
+	}
+
+	/** Sets the order for the typical buttons in a standard button bar. It is one letter per button type.
+	 * <p>
+	 * Letter in upper case will get the minimum button width that the {@link #getMinimumButtonWidth()} specifies
+	 * and letters in lower case will get the width the current look&feel specifies.
+	 * <p>
+	 * Gaps will never be added to before the first component or after the last component. However, '+' (push) will be
+	 * applied before and after as well, but with a minimum size of 0 if first/last so there will not be a gap
+	 * before or after.
+	 * <p>
+	 * If gaps are explicitly set on buttons they will never be reduced, but they may be increased.
+	 * <p>
+	 * These are the characters that can be used:
+	 * <ul>
+	 * <li><code>'L'</code> - Buttons with this style tag will staticall end up on the left end of the bar.
+	 * <li><code>'R'</code> - Buttons with this style tag will staticall end up on the right end of the bar.
+	 * <li><code>'H'</code> - A tag for the "help" button that normally is supposed to be on the right.
+	 * <li><code>'E'</code> - A tag for the "help2" button that normally is supposed to be on the left.
+	 * <li><code>'Y'</code> - A tag for the "yes" button.
+	 * <li><code>'N'</code> - A tag for the "no" button.
+	 * <li><code>'X'</code> - A tag for the "next >" or "forward >" button.
+	 * <li><code>'B'</code> - A tag for the "< back>" or "< previous" button.
+	 * <li><code>'I'</code> - A tag for the "finish".
+	 * <li><code>'A'</code> - A tag for the "apply" button.
+	 * <li><code>'C'</code> - A tag for the "cancel" or "close" button.
+	 * <li><code>'O'</code> - A tag for the "ok" or "done" button.
+	 * <li><code>'U'</code> - All Uncategorized, Other, or "Unknown" buttons. Tag will be "other".
+	 * <li><code>'+'</code> - A glue push gap that will take as much space as it can and at least an "unrelated" gap. (Platform dependant)
+	 * <li><code>'_'</code> - (underscore) An "unrelated" gap. (Platform dependant)
+	 * </ul>
+	 * <p>
+	 * Even though the style tags are normally applied to buttons this works with all components.
+	 * <p>
+	 * The normal style for MAC OS X is <code>"L_HE+U+FBI_NYCOA_R"</code>,
+	 * for Windows is <code>"L_E+U+FBI_YNOCAH_R"</code>, and for GNOME is
+	 * <code>"L_HE+UNYACBXIO_R"</code>.
+	 *
+	 * @param order The new button order for the current platform.
+	 */
+	public static void setButtonOrder(String order)
+	{
+		BUTTON_FORMAT = order;
+		MOD_COUNT++;
+	}
+
+	/** Returns the tag (used in the {@link CC}) for a char. The char is same as used in {@link #getButtonOrder()}.
+	 * @param c The char. Must be lower case!
+	 * @return The tag that corresponds to the char or <code>null</code> if the char is unrecognized.
+	 */
+	static String getTagForChar(char c)
+	{
+		switch (c) {
+			case 'o':
+				return "ok";
+			case 'c':
+				return "cancel";
+			case 'h':
+				return "help";
+			case 'e':
+				return "help2";
+			case 'y':
+				return "yes";
+			case 'n':
+				return "no";
+			case 'a':
+				return "apply";
+			case 'x':
+				return "next";  // a.k.a forward
+			case 'b':
+				return "back";  // a.k.a. previous
+			case 'i':
+				return "finish";
+			case 'l':
+				return "left";
+			case 'r':
+				return "right";
+			case 'u':
+				return "other";
+			default:
+				return null;
+		}
+	}
+
+	/** Returns the platform recommended inter-cell gap in the horizontal (x) dimension..
+	 * @return The platform recommended inter-cell gap in the horizontal (x) dimension..
+	 */
+	public static BoundSize getGridGapX()
+	{
+		return DEF_HGAP;
+	}
+
+	/** Returns the platform recommended inter-cell gap in the vertical (x) dimension..
+	 * @return The platform recommended inter-cell gap in the vertical (x) dimension..
+	 */
+	public static BoundSize getGridGapY()
+	{
+		return DEF_VGAP;
+	}
+
+	/** Returns the default dialog inset depending of the current platform.
+	 * @param side top == 0, left == 1, bottom = 2, right = 3.
+	 * @return The inset. Never <code>null</code>.
+	 */
+	public static UnitValue getDialogInsets(int side)
+	{
+		return DIALOG_INS[side];
+	}
+
+	/** Sets the default insets for a dialog. Values that are null will not be changed.
+	 * @param top The top inset. May be <code>null</code>.
+	 * @param left The left inset. May be <code>null</code>.
+	 * @param bottom The bottom inset. May be <code>null</code>.
+	 * @param right The right inset. May be <code>null</code>.
+	 */
+	public static void setDialogInsets(UnitValue top, UnitValue left, UnitValue bottom, UnitValue right)
+	{
+		if (top != null)
+			DIALOG_INS[0] = top;
+
+		if (left != null)
+			DIALOG_INS[1] = left;
+
+		if (bottom != null)
+			DIALOG_INS[2] = bottom;
+
+		if (right != null)
+			DIALOG_INS[3] = right;
+
+		MOD_COUNT++;
+	}
+
+	/** Returns the default panel inset depending of the current platform.
+	 * @param side top == 0, left == 1, bottom = 2, right = 3.
+	 * @return The inset. Never <code>null</code>.
+	 */
+	public static UnitValue getPanelInsets(int side)
+	{
+		return PANEL_INS[side];
+	}
+
+	/** Sets the default insets for a dialog. Values that are null will not be changed.
+	 * @param top The top inset. May be <code>null</code>.
+	 * @param left The left inset. May be <code>null</code>.
+	 * @param bottom The bottom inset. May be <code>null</code>.
+	 * @param right The right inset. May be <code>null</code>.
+	 */
+	public static void setPanelInsets(UnitValue top, UnitValue left, UnitValue bottom, UnitValue right)
+	{
+		if (top != null)
+			PANEL_INS[0] = top;
+
+		if (left != null)
+			PANEL_INS[1] = left;
+
+		if (bottom != null)
+			PANEL_INS[2] = bottom;
+
+		if (right != null)
+			PANEL_INS[3] = right;
+
+		MOD_COUNT++;
+	}
+
+	/** Returns the percentage used for alignment for labels (0 is left, 50 is center and 100 is right).
+	 * @return The percentage used for alignment for labels
+	 */
+	public static float getLabelAlignPercentage()
+	{
+		return CUR_PLAF == MAC_OSX ? 1f : 0f;
+	}
+
+	/** Returns the default gap between two components that <b>are in the same cell</b>.
+	 * @param comp The component that the gap is for. Never <code>null</code>.
+	 * @param adjacentComp The adjacent component if any. May be <code>null</code>.
+	 * @param adjacentSide What side the <code>adjacentComp</code> is on. {@link javax.swing.SwingUtilities#TOP} or
+	 * {@link javax.swing.SwingUtilities#LEFT} or {@link javax.swing.SwingUtilities#BOTTOM} or {@link javax.swing.SwingUtilities#RIGHT}.
+	 * @param tag The tag string that the component might be tagged with in the component constraints. May be <code>null</code>.
+	 * @param isLTR If it is left-to-right.
+	 * @return The default gap between two components or <code>null</code> if there should be no gap.
+	 */
+	static BoundSize getDefaultComponentGap(ComponentWrapper comp, ComponentWrapper adjacentComp, int adjacentSide, String tag, boolean isLTR)
+	{
+		if (GAP_PROVIDER != null)
+			return GAP_PROVIDER.getDefaultGap(comp, adjacentComp, adjacentSide, tag, isLTR);
+
+		if (adjacentComp == null)
+			return null;
+
+//		if (adjacentComp == null || adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.TOP)
+//			return null;
+
+		return (adjacentSide == SwingConstants.LEFT || adjacentSide == SwingConstants.RIGHT) ? RELATED_X : RELATED_Y;
+	}
+
+	/** Returns the current gap provider or <code>null</code> if none is set and "related" should always be used.
+	 * @return The current gap provider or <code>null</code> if none is set and "related" should always be used.
+	 */
+	public static InCellGapProvider getGapProvider()
+	{
+		return GAP_PROVIDER;
+	}
+
+	/** Sets the current gap provider or <code>null</code> if none is set and "related" should always be used.
+	 * @param provider The current gap provider or <code>null</code> if none is set and "related" should always be used.
+	 */
+	public static void setGapProvider(InCellGapProvider provider)
+	{
+		GAP_PROVIDER = provider;
+	}
+
+	/** Returns how many times the defaults has been changed. This can be used as a light weight check to
+	 * see if layout caches needs to be refreshed.
+	 * @return How many times the defaults has been changed.
+	 */
+	public static int getModCount()
+	{
+		return MOD_COUNT;
+	}
+
+	/** Tells all layout manager instances to revalidate and recalculated everything.
+	 */
+	public void invalidate()
+	{
+		MOD_COUNT++;
+	}
+
+	/** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+	 * @return The current default unit.
+	 * @see UnitValue#PIXEL
+	 * @see UnitValue#LPX
+	 */
+	public static int getDefaultHorizontalUnit()
+	{
+		return DEF_H_UNIT;
+	}
+
+	/** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+	 * @param unit The new default unit.
+	 * @see UnitValue#PIXEL
+	 * @see UnitValue#LPX
+	 */
+	public static void setDefaultHorizontalUnit(int unit)
+	{
+		if (unit < UnitValue.PIXEL || unit > UnitValue.LABEL_ALIGN)
+			throw new IllegalArgumentException("Illegal Unit: " + unit);
+
+		if (DEF_H_UNIT != unit) {
+			DEF_H_UNIT = unit;
+			MOD_COUNT++;
+		}
+	}
+
+	/** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+	 * @return The current default unit.
+	 * @see UnitValue#PIXEL
+	 * @see UnitValue#LPY
+	 */
+	public static int getDefaultVerticalUnit()
+	{
+		return DEF_V_UNIT;
+	}
+
+	/** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+	 * @param unit The new default unit.
+	 * @see UnitValue#PIXEL
+	 * @see UnitValue#LPY
+	 */
+	public static void setDefaultVerticalUnit(int unit)
+	{
+		if (unit < UnitValue.PIXEL || unit > UnitValue.LABEL_ALIGN)
+			throw new IllegalArgumentException("Illegal Unit: " + unit);
+
+		if (DEF_V_UNIT != unit) {
+			DEF_V_UNIT = unit;
+			MOD_COUNT++;
+		}
+	}
+
+	/** The default alignment for rows. Pre v3.5 this was <code>false</code> but now it is
+	 * <code>true</code>.
+	 * @return The current value. Default is <code>true</code>.
+	 * @since 3.5
+	 */
+	public static boolean getDefaultRowAlignmentBaseline()
+	{
+		return dra;
+	}
+
+	/** The default alignment for rows. Pre v3.5 this was <code>false</code> but now it is
+	 * <code>true</code>.
+	 * @param b The new value. Default is <code>true</code> from v3.5.
+	 * @since 3.5
+	 */
+	public static void setDefaultRowAlignmentBaseline(boolean b)
+	{
+		dra = b;
+	}
+}
diff --git a/src/net/miginfocom/layout/ResizeConstraint.java b/src/net/miginfocom/layout/ResizeConstraint.java
new file mode 100644
index 0000000..75a4c37
--- /dev/null
+++ b/src/net/miginfocom/layout/ResizeConstraint.java
@@ -0,0 +1,92 @@
+package net.miginfocom.layout;
+
+import java.io.*;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/** A parsed constraint that specifies how an entity (normally column/row or component) can shrink or
+ * grow compared to other entities.
+ */
+final class ResizeConstraint implements Externalizable
+{
+	static final Float WEIGHT_100 = new Float(100);
+
+	/** How flexilble the entity should be, relative to other entities, when it comes to growing. <code>null</code> or
+	 * zero mean it will never grow. An entity that has twise the growWeight compared to another entity will get twice
+	 * as much of available space.
+	 * <p>
+	 * "grow" are only compared within the same "growPrio".
+	 */
+	Float grow = null;
+
+	/** The relative priority used for determining which entities gets the extra space first.
+	 */
+	int growPrio = 100;
+
+	Float shrink = WEIGHT_100;
+
+	int shrinkPrio = 100;
+
+	public ResizeConstraint()   // For Externalizable
+	{
+	}
+
+	ResizeConstraint(int shrinkPrio, Float shrinkWeight, int growPrio, Float growWeight)
+	{
+		this.shrinkPrio = shrinkPrio;
+		this.shrink = shrinkWeight;
+		this.growPrio = growPrio;
+		this.grow = growWeight;
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+
+	public void writeExternal(ObjectOutput out) throws IOException
+	{
+		if (getClass() == ResizeConstraint.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+}
diff --git a/src/net/miginfocom/layout/UnitConverter.java b/src/net/miginfocom/layout/UnitConverter.java
new file mode 100644
index 0000000..e94e82b
--- /dev/null
+++ b/src/net/miginfocom/layout/UnitConverter.java
@@ -0,0 +1,59 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+/**
+ */
+public abstract class UnitConverter
+{
+	/** Value to return if this converter can not handle the <code>unit</code> sent in as an argument
+	 * to the convert method.
+	 */
+	public static final int UNABLE = -87654312;
+
+	/** Converts <code>value</code> to pixels.
+	 * @param value The value to be converted.
+	 * @param unit The unit of <code>value</code>. Never <code>null</code> and at least one character.
+	 * @param refValue Some reference value that may of may not be used. If the unit is percent for instance this value
+	 * is the value to take the percent from. Usually the size of the parent component in the appropriate dimension.
+	 * @param isHor If the value is horizontal (<code>true</code>) or vertical (<code>false</code>).
+	 * @param parent The parent of the target component that <code>value</code> is to be applied to.
+	 * Might for instance be needed to get the screen that the component is on in a multi screen environment.
+	 * <p>
+	 * May be <code>null</code> in which case a "best guess" value should be returned.
+	 * @param comp The component, if applicable, or <code>null</code> if none.
+	 * @return The number of pixels if <code>unit</code> is handled by this converter, <code>UnitConverter.UNABLE</code> if not.
+	 */
+	public abstract int convertToPixels(float value, String unit, boolean isHor, float refValue, ContainerWrapper parent, ComponentWrapper comp);
+}
diff --git a/src/net/miginfocom/layout/UnitValue.java b/src/net/miginfocom/layout/UnitValue.java
new file mode 100644
index 0000000..82cd752
--- /dev/null
+++ b/src/net/miginfocom/layout/UnitValue.java
@@ -0,0 +1,637 @@
+package net.miginfocom.layout;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+import java.beans.Encoder;
+import java.beans.Expression;
+import java.beans.PersistenceDelegate;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public final class UnitValue implements Serializable
+{
+	private static final HashMap<String, Integer> UNIT_MAP = new HashMap<String, Integer>(32);
+
+	private static final ArrayList<UnitConverter> CONVERTERS = new ArrayList<UnitConverter>();
+
+	/** An operation indicating a static value.
+	 */
+	public static final int STATIC = 100;
+
+	/** An operation indicating a addition of two sub units.
+	 */
+	public static final int ADD = 101; // Must have "sub-unit values"
+
+	/** An operation indicating a subtraction of two sub units
+	 */
+	public static final int SUB = 102; // Must have "sub-unit values"
+
+	/** An operation indicating a multiplication of two sub units.
+	 */
+	public static final int MUL = 103; // Must have "sub-unit values"
+
+	/** An operation indicating a division of two sub units.
+	 */
+	public static final int DIV = 104; // Must have "sub-unit values"
+
+	/** An operation indicating the minimum of two sub units
+	 */
+	public static final int MIN = 105; // Must have "sub-unit values"
+
+	/** An operation indicating the maximum of two sub units
+	 */
+	public static final int MAX = 106; // Must have "sub-unit values"
+
+	/** An operation indicating the middle value of two sub units
+	 */
+	public static final int MID = 107; // Must have "sub-unit values"
+
+
+
+
+	/** A unit indicating pixels.
+	 */
+	public static final int PIXEL = 0;
+
+	/** A unit indicating logical horizontal pixels.
+	 */
+	public static final int LPX = 1;
+
+	/** A unit indicating logical vertical pixels.
+	 */
+	public static final int LPY = 2;
+
+	/** A unit indicating millimeters.
+	 */
+	public static final int MM = 3;
+
+	/** A unit indicating centimeters.
+	 */
+	public static final int CM = 4;
+
+	/** A unit indicating inches.
+	 */
+	public static final int INCH = 5;
+
+	/** A unit indicating percent.
+	 */
+	public static final int PERCENT = 6;
+
+	/** A unit indicating points.
+	 */
+	public static final int PT = 7;
+
+	/** A unit indicating screen percentage width.
+	 */
+	public static final int SPX = 8;
+
+	/** A unit indicating screen percentage height.
+	 */
+	public static final int SPY = 9;
+
+	/** A unit indicating alignment.
+	 */
+	public static final int ALIGN = 12;
+
+	/** A unit indicating minimum size.
+	 */
+	public static final int MIN_SIZE = 13;
+
+	/** A unit indicating preferred size.
+	 */
+	public static final int PREF_SIZE = 14;
+
+	/** A unit indicating maximum size.
+	 */
+	public static final int MAX_SIZE = 15;
+
+	/** A unit indicating botton size.
+	 */
+	public static final int BUTTON = 16;
+
+	/** A unit indicating linking to x.
+	 */
+	public static final int LINK_X = 18;   // First link
+
+	/** A unit indicating linking to y.
+	 */
+	public static final int LINK_Y = 19;
+
+	/** A unit indicating linking to width.
+	 */
+	public static final int LINK_W = 20;
+
+	/** A unit indicating linking to height.
+	 */
+	public static final int LINK_H = 21;
+
+	/** A unit indicating linking to x2.
+	 */
+	public static final int LINK_X2 = 22;
+
+	/** A unit indicating linking to y2.
+	 */
+	public static final int LINK_Y2 = 23;
+
+	/** A unit indicating linking to x position on screen.
+	 */
+	public static final int LINK_XPOS = 24;
+
+	/** A unit indicating linking to y position on screen.
+	 */
+	public static final int LINK_YPOS = 25;    // Last link
+
+	/** A unit indicating a lookup.
+	 */
+	public static final int LOOKUP = 26;
+
+	/** A unit indicating label alignment.
+	 */
+	public static final int LABEL_ALIGN = 27;
+
+	private static final int IDENTITY = -1;
+
+	static {
+		UNIT_MAP.put("px", PIXEL);
+		UNIT_MAP.put("lpx", LPX);
+		UNIT_MAP.put("lpy", LPY);
+		UNIT_MAP.put("%", PERCENT);
+		UNIT_MAP.put("cm", CM);
+		UNIT_MAP.put("in", INCH);
+		UNIT_MAP.put("spx", SPX);
+		UNIT_MAP.put("spy", SPY);
+		UNIT_MAP.put("al", ALIGN);
+		UNIT_MAP.put("mm", MM);
+		UNIT_MAP.put("pt", PT);
+		UNIT_MAP.put("min", MIN_SIZE);
+		UNIT_MAP.put("minimum", MIN_SIZE);
+		UNIT_MAP.put("p", PREF_SIZE);
+		UNIT_MAP.put("pref", PREF_SIZE);
+		UNIT_MAP.put("max", MAX_SIZE);
+		UNIT_MAP.put("maximum", MAX_SIZE);
+		UNIT_MAP.put("button", BUTTON);
+		UNIT_MAP.put("label", LABEL_ALIGN);
+	}
+
+	static final UnitValue ZERO = new UnitValue(0, null, PIXEL, true, STATIC, null, null, "0px");
+	static final UnitValue TOP = new UnitValue(0, null, PERCENT, false, STATIC, null, null, "top");
+	static final UnitValue LEADING = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "leading");
+	static final UnitValue LEFT = new UnitValue(0, null, PERCENT, true, STATIC, null, null, "left");
+	static final UnitValue CENTER = new UnitValue(50, null, PERCENT, true, STATIC, null, null, "center");
+	static final UnitValue TRAILING = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "trailing");
+	static final UnitValue RIGHT = new UnitValue(100, null, PERCENT, true, STATIC, null, null, "right");
+	static final UnitValue BOTTOM = new UnitValue(100, null, PERCENT, false, STATIC, null, null, "bottom");
+	static final UnitValue LABEL = new UnitValue(0, null, LABEL_ALIGN, false, STATIC, null, null, "label");
+
+	static final UnitValue INF = new UnitValue(LayoutUtil.INF, null, PIXEL, true, STATIC, null, null, "inf");
+
+	static final UnitValue BASELINE_IDENTITY = new UnitValue(0, null, IDENTITY, false, STATIC, null, null, "baseline");
+
+	private final transient float value;
+	private final transient int unit;
+	private final transient int oper;
+	private final transient String unitStr;
+	private transient String linkId = null; // Should be final, but initializes in a sub method.
+	private final transient boolean isHor;
+	private final transient UnitValue[] subUnits;
+
+	// Pixel
+	public UnitValue(float value)  // If hor/ver does not matter.
+	{
+		this(value, null, PIXEL, true, STATIC, null, null, value + "px");
+	}
+
+	public UnitValue(float value, int unit, String createString)  // If hor/ver does not matter.
+	{
+		this(value, null, unit, true, STATIC, null, null, createString);
+	}
+
+	UnitValue(float value, String unitStr, boolean isHor, int oper, String createString)
+	{
+		this(value, unitStr, -1, isHor, oper, null, null, createString);
+	}
+
+	UnitValue(boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
+	{
+		this(0, "", -1, isHor, oper, sub1, sub2, createString);
+		if (sub1 == null || sub2 == null)
+			throw new IllegalArgumentException("Sub units is null!");
+	}
+
+	private UnitValue(float value, String unitStr, int unit, boolean isHor, int oper, UnitValue sub1, UnitValue sub2, String createString)
+	{
+		if (oper < STATIC || oper > MID)
+			throw new IllegalArgumentException("Unknown Operation: " + oper);
+
+		if (oper >= ADD && oper <= MID && (sub1 == null || sub2 == null))
+			throw new IllegalArgumentException(oper + " Operation may not have null sub-UnitValues.");
+
+		this.value = value;
+		this.oper = oper;
+		this.isHor = isHor;
+		this.unitStr = unitStr;
+		this.unit = unitStr != null ? parseUnitString() : unit;
+		this.subUnits = sub1 != null && sub2 != null ? new UnitValue[] {sub1, sub2} : null;
+
+		LayoutUtil.putCCString(this, createString);    // "this" escapes!! Safe though.
+	}
+
+	/** Returns the size in pixels rounded.
+	 * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
+	 * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
+	 * required to return any usable value if <code>null</code>.
+	 * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
+	 * connected to any component.
+	 * @return The size in pixels.
+	 */
+	public final int getPixels(float refValue, ContainerWrapper parent, ComponentWrapper comp)
+	{
+		return Math.round(getPixelsExact(refValue, parent, comp));
+	}
+
+	private static final float[] SCALE = new float[] {25.4f, 2.54f, 1f, 0f, 72f};
+	/** Returns the size in pixels.
+	 * @param refValue The reference value. Normally the size of the parent. For unit {@link #ALIGN} the current size of the component should be sent in.
+	 * @param parent The parent. May be <code>null</code> for testing the validity of the value, but should normally not and are not
+	 * required to return any usable value if <code>null</code>.
+	 * @param comp The component, if any, that the value is for. Might be <code>null</code> if the value is not
+	 * connected to any component.
+	 * @return The size in pixels.
+	 */
+	public final float getPixelsExact(float refValue, ContainerWrapper parent, ComponentWrapper comp)
+	{
+		if (parent == null)
+			return 1;
+
+		if (oper == STATIC) {
+			switch (unit) {
+				case PIXEL:
+					return value;
+
+				case LPX:
+				case LPY:
+					return parent.getPixelUnitFactor(unit == LPX) * value;
+
+				case MM:
+				case CM:
+				case INCH:
+				case PT:
+					float f = SCALE[unit - MM];
+					Float s = isHor ? PlatformDefaults.getHorizontalScaleFactor() : PlatformDefaults.getVerticalScaleFactor();
+					if (s != null)
+						f *= s;
+					return (isHor ? parent.getHorizontalScreenDPI() : parent.getVerticalScreenDPI()) * value / f;
+
+				case PERCENT:
+					return value * refValue * 0.01f;
+
+				case SPX:
+				case SPY:
+					return (unit == SPX ? parent.getScreenWidth() : parent.getScreenHeight()) * value * 0.01f;
+
+				case ALIGN:
+					Integer st = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.X : LinkHandler.Y);
+					Integer sz = LinkHandler.getValue(parent.getLayout(), "visual", isHor ? LinkHandler.WIDTH : LinkHandler.HEIGHT);
+					if (st == null || sz == null)
+						return 0;
+					return value * (Math.max(0, sz.intValue()) - refValue) + st;
+
+				case MIN_SIZE:
+					if (comp == null)
+						return 0;
+					return isHor ? comp.getMinimumWidth(comp.getHeight()) : comp.getMinimumHeight(comp.getWidth());
+
+				case PREF_SIZE:
+					if (comp == null)
+						return 0;
+					return isHor ? comp.getPreferredWidth(comp.getHeight()) : comp.getPreferredHeight(comp.getWidth());
+
+				case MAX_SIZE:
+					if (comp == null)
+						return 0;
+					return isHor ? comp.getMaximumWidth(comp.getHeight()) : comp.getMaximumHeight(comp.getWidth());
+
+				case BUTTON:
+					return PlatformDefaults.getMinimumButtonWidth().getPixels(refValue, parent, comp);
+
+				case LINK_X:
+				case LINK_Y:
+				case LINK_W:
+				case LINK_H:
+				case LINK_X2:
+				case LINK_Y2:
+				case LINK_XPOS:
+				case LINK_YPOS:
+					Integer v = LinkHandler.getValue(parent.getLayout(), getLinkTargetId(), unit - (unit >= LINK_XPOS ? LINK_XPOS : LINK_X));
+					if (v == null)
+						return 0;
+
+					if (unit == LINK_XPOS)
+						return parent.getScreenLocationX() + v;
+					if (unit == LINK_YPOS)
+						return parent.getScreenLocationY() + v;
+
+					return v;
+
+				case LOOKUP:
+					float res = lookup(refValue, parent, comp);
+					if (res != UnitConverter.UNABLE)
+						return res;
+
+				case LABEL_ALIGN:
+					return PlatformDefaults.getLabelAlignPercentage() * refValue;
+
+				case IDENTITY:
+			}
+			throw new IllegalArgumentException("Unknown/illegal unit: " + unit + ", unitStr: " + unitStr);
+		}
+
+		if (subUnits != null && subUnits.length == 2) {
+			float r1 = subUnits[0].getPixelsExact(refValue, parent, comp);
+			float r2 = subUnits[1].getPixelsExact(refValue, parent, comp);
+			switch (oper) {
+				case ADD:
+					return r1 + r2;
+				case SUB:
+					return r1 - r2;
+				case MUL:
+					return r1 * r2;
+				case DIV:
+					return r1 / r2;
+				case MIN:
+					return r1 < r2 ? r1 : r2;
+				case MAX:
+					return r1 > r2 ? r1 : r2;
+				case MID:
+					return (r1 + r2) * 0.5f;
+			}
+		}
+
+		throw new IllegalArgumentException("Internal: Unknown Oper: " + oper);
+	}
+
+	private float lookup(float refValue, ContainerWrapper parent, ComponentWrapper comp)
+	{
+		float res = UnitConverter.UNABLE;
+		for (int i = CONVERTERS.size() - 1; i >= 0; i--) {
+			res = CONVERTERS.get(i).convertToPixels(value, unitStr, isHor, refValue, parent, comp);
+			if (res != UnitConverter.UNABLE)
+				return res;
+		}
+		return PlatformDefaults.convertToPixels(value, unitStr, isHor, refValue, parent, comp);
+	}
+
+	private int parseUnitString()
+	{
+		int len = unitStr.length();
+		if (len == 0)
+			return isHor ? PlatformDefaults.getDefaultHorizontalUnit() : PlatformDefaults.getDefaultVerticalUnit();
+
+		Integer u = UNIT_MAP.get(unitStr);
+		if (u != null)
+			return u;
+
+		if (unitStr.equals("lp"))
+			return isHor ? LPX : LPY;
+
+		if (unitStr.equals("sp"))
+			return isHor ? SPX : SPY;
+
+		if (lookup(0, null, null) != UnitConverter.UNABLE)    // To test so we can fail fast
+			return LOOKUP;
+
+		// Only link left. E.g. "otherID.width"
+
+		int pIx = unitStr.indexOf('.');
+		if (pIx != -1) {
+			linkId = unitStr.substring(0, pIx);
+			String e = unitStr.substring(pIx + 1);
+
+			if (e.equals("x"))
+				return LINK_X;
+			if (e.equals("y"))
+				return LINK_Y;
+			if (e.equals("w") || e.equals("width"))
+				return LINK_W;
+			if (e.equals("h") || e.equals("height"))
+				return LINK_H;
+			if (e.equals("x2"))
+				return LINK_X2;
+			if (e.equals("y2"))
+				return LINK_Y2;
+			if (e.equals("xpos"))
+				return LINK_XPOS;
+			if (e.equals("ypos"))
+				return LINK_YPOS;
+		}
+
+		throw new IllegalArgumentException("Unknown keyword: " + unitStr);
+	}
+
+	final boolean isLinked()
+	{
+		return linkId != null;
+	}
+
+	final boolean isLinkedDeep()
+	{
+		if (subUnits == null)
+			return linkId != null;
+
+		for (UnitValue subUnit : subUnits) {
+			if (subUnit.isLinkedDeep())
+				return true;
+		}
+
+		return false;
+	}
+
+	final String getLinkTargetId()
+	{
+		return linkId;
+	}
+
+	final UnitValue getSubUnitValue(int i)
+	{
+		return subUnits[i];
+	}
+
+	final int getSubUnitCount()
+	{
+		return subUnits != null ? subUnits.length : 0;
+	}
+
+	public final UnitValue[] getSubUnits()
+	{
+		return subUnits != null ? subUnits.clone() : null;
+	}
+
+	public final int getUnit()
+	{
+		return unit;
+	}
+
+	public final String getUnitString()
+	{
+		return unitStr;
+	}
+
+	public final int getOperation()
+	{
+		return oper;
+	}
+
+	public final float getValue()
+	{
+		return value;
+	}
+
+	public final boolean isHorizontal()
+	{
+		return isHor;
+	}
+
+	final public String toString()
+	{
+		return getClass().getName() + ". Value=" + value + ", unit=" + unit + ", unitString: " + unitStr + ", oper=" + oper + ", isHor: " + isHor;
+	}
+
+	/** Returns the creation string for this object. Note that {@link LayoutUtil#setDesignTime(ContainerWrapper, boolean)} must be
+	 * set to <code>true</code> for the creation strings to be stored.
+	 * @return The constraint string or <code>null</code> if none is registered.
+	 */
+	public final String getConstraintString()
+	{
+		return LayoutUtil.getCCString(this);
+	}
+
+	public final int hashCode()
+	{
+		return (int) (value * 12345) + (oper >>> 5) + unit >>> 17;
+	}
+
+	/** Adds a global unit converter that can convert from some <code>unit</code> to pixels.
+	 * <p>
+	 * This converter will be asked before the platform converter so the values for it (e.g. "related" and "unrelated")
+	 * can be overridden. It is however not possible to override the built in ones (e.g. "mm", "pixel" or "lp").
+	 * @param conv The converter. Not <code>null</code>.
+	 */
+	public synchronized static void addGlobalUnitConverter(UnitConverter conv)
+	{
+		if (conv == null)
+			throw new NullPointerException();
+		CONVERTERS.add(conv);
+	}
+
+	/** Removed the converter.
+	 * @param unit The converter.
+	 * @return If there was a converter found and thus removed.
+	 */
+	public synchronized static boolean removeGlobalUnitConverter(UnitConverter unit)
+	{
+		return CONVERTERS.remove(unit);
+	}
+
+	/** Returns the global converters currently registered. The platform converter will not be in this list.
+	 * @return The converters. Never <code>null</code>.
+	 */
+	public synchronized static UnitConverter[] getGlobalUnitConverters()
+	{
+		return CONVERTERS.toArray(new UnitConverter[CONVERTERS.size()]);
+	}
+
+	/** Returns the current default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+	 * @return The current default unit.
+	 * @see #PIXEL
+	 * @see #LPX
+	 * @deprecated Use {@link PlatformDefaults#getDefaultHorizontalUnit()} and {@link PlatformDefaults#getDefaultVerticalUnit()} instead.
+	 */
+	public static int getDefaultUnit()
+	{
+		return PlatformDefaults.getDefaultHorizontalUnit();
+	}
+
+	/** Sets the default unit. The default unit is the unit used if no unit is set. E.g. "width 10".
+	 * @param unit The new default unit.
+	 * @see #PIXEL
+	 * @see #LPX
+	 * @deprecated Use {@link PlatformDefaults#setDefaultHorizontalUnit(int)} and {@link PlatformDefaults#setDefaultVerticalUnit(int)} instead.
+	 */
+	public static void setDefaultUnit(int unit)
+	{
+		PlatformDefaults.setDefaultHorizontalUnit(unit);
+		PlatformDefaults.setDefaultVerticalUnit(unit);
+	}
+
+	static {
+        if(LayoutUtil.HAS_BEANS){
+            LayoutUtil.setDelegate(UnitValue.class, new PersistenceDelegate() {
+                protected Expression instantiate(Object oldInstance, Encoder out)
+                {
+                    UnitValue uv = (UnitValue) oldInstance;
+                    String cs = uv.getConstraintString();
+                    if (cs == null)
+                        throw new IllegalStateException("Design time must be on to use XML persistence. See LayoutUtil.");
+
+                    return new Expression(oldInstance, ConstraintParser.class, "parseUnitValueOrAlign", new Object[] {
+                            uv.getConstraintString(), (uv.isHorizontal() ? Boolean.TRUE : Boolean.FALSE), null
+                    });
+                }
+            });
+        }
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private static final long serialVersionUID = 1L;
+
+	private Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	private void writeObject(ObjectOutputStream out) throws IOException
+	{
+		if (getClass() == UnitValue.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+
+	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+}
diff --git a/src/net/miginfocom/swing/MigLayout.java b/src/net/miginfocom/swing/MigLayout.java
new file mode 100644
index 0000000..d8d5e54
--- /dev/null
+++ b/src/net/miginfocom/swing/MigLayout.java
@@ -0,0 +1,701 @@
+package net.miginfocom.swing;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+import net.miginfocom.layout.*;
+
+import javax.swing.*;
+import javax.swing.Timer;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.*;
+import java.util.*;
+
+/** A very flexible layout manager.
+ * <p>
+ * Read the documentation that came with this layout manager for information on usage.
+ */
+public final class MigLayout implements LayoutManager2, Externalizable
+{
+	// ******** Instance part ********
+
+	/** The component to string constraints mappings.
+	 */
+	private final Map<Component, Object> scrConstrMap = new IdentityHashMap<Component, Object>(8);
+
+	/** Hold the serializable text representation of the constraints.
+	 */
+	private Object layoutConstraints = "", colConstraints = "", rowConstraints = "";    // Should never be null!
+
+	// ******** Transient part ********
+
+	private transient ContainerWrapper cacheParentW = null;
+
+	private transient final Map<ComponentWrapper, CC> ccMap = new HashMap<ComponentWrapper, CC>(8);
+	private transient javax.swing.Timer debugTimer = null;
+
+	private transient LC lc = null;
+	private transient AC colSpecs = null, rowSpecs = null;
+	private transient Grid grid = null;
+	private transient int lastModCount = PlatformDefaults.getModCount();
+	private transient int lastHash = -1;
+	private transient Dimension lastInvalidSize = null;
+	private transient boolean lastWasInvalid = false;  // Added in 3.7.1. May have regressions
+	private transient Dimension lastParentSize = null;
+
+	private transient ArrayList<LayoutCallback> callbackList = null;
+
+	private transient boolean dirty = true;
+
+	/** Constructor with no constraints.
+	 */
+	public MigLayout()
+	{
+		this("", "", "");
+	}
+
+	/** Constructor.
+	 * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
+	 */
+	public MigLayout(String layoutConstraints)
+	{
+		this(layoutConstraints, "", "");
+	}
+
+	/** Constructor.
+	 * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
+	 * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
+	 */
+	public MigLayout(String layoutConstraints, String colConstraints)
+	{
+		this(layoutConstraints, colConstraints, "");
+	}
+
+	/** Constructor.
+	 * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as "".
+	 * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as "".
+	 * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as "".
+	 */
+	public MigLayout(String layoutConstraints, String colConstraints, String rowConstraints)
+	{
+		setLayoutConstraints(layoutConstraints);
+		setColumnConstraints(colConstraints);
+		setRowConstraints(rowConstraints);
+	}
+
+	/** Constructor.
+	 * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
+	 */
+	public MigLayout(LC layoutConstraints)
+	{
+		this(layoutConstraints, null, null);
+	}
+
+	/** Constructor.
+	 * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
+	 * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
+	 */
+	public MigLayout(LC layoutConstraints, AC colConstraints)
+	{
+		this(layoutConstraints, colConstraints, null);
+	}
+
+	/** Constructor.
+	 * @param layoutConstraints The constraints that concern the whole layout. <code>null</code> will be treated as an empty constraint.
+	 * @param colConstraints The constraints for the columns in the grid. <code>null</code> will be treated as an empty constraint.
+	 * @param rowConstraints The constraints for the rows in the grid. <code>null</code> will be treated as an empty constraint.
+	 */
+	public MigLayout(LC layoutConstraints, AC colConstraints, AC rowConstraints)
+	{
+		setLayoutConstraints(layoutConstraints);
+		setColumnConstraints(colConstraints);
+		setRowConstraints(rowConstraints);
+	}
+
+	/** Returns layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
+	 * to the constructor or set with {@link #setLayoutConstraints(Object)}.
+	 * @return The layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
+	 * to the constructor or set with {@link #setLayoutConstraints(Object)}. Never <code>null</code>.
+	 */
+	public Object getLayoutConstraints()
+	{
+		return layoutConstraints;
+	}
+
+	/** Sets the layout constraints for the layout manager instance as a String.
+	 * <p>
+	 * See the class JavaDocs for information on how this string is formatted.
+	 * @param constr The layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
+	 * @throws RuntimeException if the constraint was not valid.
+	 */
+	public void setLayoutConstraints(Object constr)
+	{
+		if (constr == null || constr instanceof String) {
+			constr = ConstraintParser.prepare((String) constr);
+			lc = ConstraintParser.parseLayoutConstraint((String) constr);
+		} else if (constr instanceof LC) {
+			lc = (LC) constr;
+		} else {
+			throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
+		}
+		layoutConstraints = constr;
+		dirty = true;
+	}
+
+	/** Returns the column layout constraints either as a <code>String</code> or {@link net.miginfocom.layout.AC}.
+	 * @return The column constraints either as a <code>String</code> or {@link net.miginfocom.layout.LC} depending what was sent in
+	 * to the constructor or set with {@link #setLayoutConstraints(Object)}. Never <code>null</code>.
+	 */
+	public Object getColumnConstraints()
+	{
+		return colConstraints;
+	}
+
+	/** Sets the column layout constraints for the layout manager instance as a String.
+	 * <p>
+	 * See the class JavaDocs for information on how this string is formatted.
+	 * @param constr The column layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
+	 * @throws RuntimeException if the constraint was not valid.
+	 */
+	public void setColumnConstraints(Object constr)
+	{
+		if (constr == null || constr instanceof String) {
+			constr = ConstraintParser.prepare((String) constr);
+			colSpecs = ConstraintParser.parseColumnConstraints((String) constr);
+		} else if (constr instanceof AC) {
+			colSpecs = (AC) constr;
+		} else {
+			throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
+		}
+		colConstraints = constr;
+		dirty = true;
+	}
+
+	/** Returns the row layout constraints as a String representation. This string is the exact string as set with {@link #setRowConstraints(Object)}
+	 * or sent into the constructor.
+	 * <p>
+	 * See the class JavaDocs for information on how this string is formatted.
+	 * @return The row layout constraints as a String representation. Never <code>null</code>.
+	 */
+	public Object getRowConstraints()
+	{
+		return rowConstraints;
+	}
+
+	/** Sets the row layout constraints for the layout manager instance as a String.
+	 * <p>
+	 * See the class JavaDocs for information on how this string is formatted.
+	 * @param constr The row layout constraints as a String representation. <code>null</code> is converted to <code>""</code> for storage.
+	 * @throws RuntimeException if the constraint was not valid.
+	 */
+	public void setRowConstraints(Object constr)
+	{
+		if (constr == null || constr instanceof String) {
+			constr = ConstraintParser.prepare((String) constr);
+			rowSpecs = ConstraintParser.parseRowConstraints((String) constr);
+		} else if (constr instanceof AC) {
+			rowSpecs = (AC) constr;
+		} else {
+			throw new IllegalArgumentException("Illegal constraint type: " + constr.getClass().toString());
+		}
+		rowConstraints = constr;
+		dirty = true;
+	}
+
+	/** Returns a shallow copy of the constraints map.
+	 * @return A  shallow copy of the constraints map. Never <code>null</code>.
+	 */
+	public Map<Component, Object> getConstraintMap()
+	{
+		return new IdentityHashMap<Component, Object>(scrConstrMap);
+	}
+
+	/** Sets the constraints map.
+	 * @param map The map. Will be copied.
+	 */
+	public void setConstraintMap(Map<Component, Object> map)
+	{
+		scrConstrMap.clear();
+		ccMap.clear();
+		for (Map.Entry<Component, Object> e : map.entrySet())
+			setComponentConstraintsImpl(e.getKey(), e.getValue(), true);
+	}
+
+	/** Returns the component constraints as a String representation. This string is the exact string as set with {@link #setComponentConstraints(java.awt.Component, Object)}
+	 * or set when adding the component to the parent component.
+	 * <p>
+	 * See the class JavaDocs for information on how this string is formatted.
+	 * @param comp The component to return the constraints for.
+	 * @return The component constraints as a String representation or <code>null</code> if the component is not registered
+	 * with this layout manager. The returned values is either a String or a {@link net.miginfocom.layout.CC}
+	 * depending on what constraint was sent in when the component was added. May be <code>null</code>.
+	 */
+	public Object getComponentConstraints(Component comp)
+	{
+		synchronized(comp.getParent().getTreeLock()) {
+			return scrConstrMap.get(comp);
+		}
+	}
+
+	/** Sets the component constraint for the component that already must be handled by this layout manager.
+	 * <p>
+	 * See the class JavaDocs for information on how this string is formatted.
+	 * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
+	 * @param comp The component to set the constraints for.
+	 * @throws RuntimeException if the constraint was not valid.
+	 * @throws IllegalArgumentException If the component is not handling the component.
+	 */
+	public void setComponentConstraints(Component comp, Object constr)
+	{
+		setComponentConstraintsImpl(comp, constr, false);
+	}
+
+	/** Sets the component constraint for the component that already must be handled by this layout manager.
+	 * <p>
+	 * See the class JavaDocs for information on how this string is formatted.
+	 * @param constr The component constraints as a String or {@link net.miginfocom.layout.CC}. <code>null</code> is ok.
+	 * @param comp The component to set the constraints for.
+	 * @param noCheck Doe not check if the component is handled if true
+	 * @throws RuntimeException if the constraint was not valid.
+	 * @throws IllegalArgumentException If the component is not handling the component.
+	 */
+	private void setComponentConstraintsImpl(Component comp, Object constr, boolean noCheck)
+	{
+		Container parent = comp.getParent();
+		synchronized(parent != null ? parent.getTreeLock() : new Object()) { // 3.7.2. No sync if not added to a hierarchy. Defeats a NPE.
+			if (noCheck == false && scrConstrMap.containsKey(comp) == false)
+				throw new IllegalArgumentException("Component must already be added to parent!");
+
+			ComponentWrapper cw = new SwingComponentWrapper(comp);
+
+			if (constr == null || constr instanceof String) {
+				String cStr = ConstraintParser.prepare((String) constr);
+
+				scrConstrMap.put(comp, constr);
+				ccMap.put(cw, ConstraintParser.parseComponentConstraint(cStr));
+
+			} else if (constr instanceof CC) {
+
+				scrConstrMap.put(comp, constr);
+				ccMap.put(cw, (CC) constr);
+
+			} else {
+				throw new IllegalArgumentException("Constraint must be String or ComponentConstraint: " + constr.getClass().toString());
+			}
+
+			dirty = true;
+		}
+	}
+
+	/** Returns if this layout manager is currently managing this component.
+	 * @param c The component to check. If <code>null</code> then <code>false</code> will be returned.
+	 * @return If this layout manager is currently managing this component.
+	 */
+	public boolean isManagingComponent(Component c)
+	{
+		return scrConstrMap.containsKey(c);
+	}
+
+	/** Adds the callback function that will be called at different stages of the layout cylce.
+	 * @param callback The callback. Not <code>null</code>.
+	 */
+	public void addLayoutCallback(LayoutCallback callback)
+	{
+		if (callback == null)
+			throw new NullPointerException();
+
+		if (callbackList == null)
+			callbackList = new ArrayList<LayoutCallback>(1);
+
+		callbackList.add(callback);
+	}
+
+	/** Removes the callback if it exists.
+	 * @param callback The callback. May be <code>null</code>.
+	 */
+	public void removeLayoutCallback(LayoutCallback callback)
+	{
+		if (callbackList != null)
+			callbackList.remove(callback);
+	}
+
+	/** Sets the debugging state for this layout manager instance. If debug is turned on a timer will repaint the last laid out parent
+	 * with debug information on top.
+	 * <p>
+	 * Red fill and dashed red outline is used to indicate occupied cells in the grid. Blue dashed outline indicate
+	 * component bounds set.
+	 * <p>
+	 * Note that debug can also be set on the layout constraints. There it will be persisted. The value set here will not. See the class
+	 * JavaDocs for information.
+	 * @param parentW The parent to set debug for.
+	 * @param b <code>true</code> means debug is turned on.
+	 */
+	private void setDebug(final ComponentWrapper parentW, boolean b)
+	{
+		if (b && (debugTimer == null || debugTimer.getDelay() != getDebugMillis())) {
+			if (debugTimer != null)
+				debugTimer.stop();
+
+			ContainerWrapper pCW = parentW.getParent();
+			final Component parent = pCW != null ? (Component) pCW.getComponent() : null;
+
+			debugTimer = new Timer(getDebugMillis(), new MyDebugRepaintListener());
+
+			if (parent != null) {
+				SwingUtilities.invokeLater(new Runnable() {
+					public void run() {
+						Container p = parent.getParent();
+						if (p != null) {
+							if (p instanceof JComponent) {
+								((JComponent) p).revalidate();
+							} else {
+								parent.invalidate();
+								p.validate();
+							}
+						}
+					}
+				});
+			}
+
+			debugTimer.setInitialDelay(100);
+			debugTimer.start();
+
+		} else if (!b && debugTimer != null) {
+			debugTimer.stop();
+			debugTimer = null;
+		}
+	}
+
+	/** Returns the current debugging state.
+	 * @return The current debugging state.
+	 */
+	private boolean getDebug()
+	{
+		return debugTimer != null;
+	}
+
+	/** Returns the debug millis. Combines the value from {@link net.miginfocom.layout.LC#getDebugMillis()} and {@link net.miginfocom.layout.LayoutUtil#getGlobalDebugMillis()}
+	 * @return The combined value.
+	 */
+	private int getDebugMillis()
+	{
+		int globalDebugMillis = LayoutUtil.getGlobalDebugMillis();
+		return globalDebugMillis > 0 ? globalDebugMillis : lc.getDebugMillis();
+	}
+
+	/** Check if something has changed and if so recreate it to the cached objects.
+	 * @param parent The parent that is the target for this layout manager.
+	 */
+	private void checkCache(Container parent)
+	{
+		if (parent == null)
+			return;
+
+		if (dirty)
+			grid = null;
+
+		// Check if the grid is valid
+		int mc = PlatformDefaults.getModCount();
+		if (lastModCount != mc) {
+			grid = null;
+			lastModCount = mc;
+		}
+
+		if (parent.isValid() == false) {
+			if (lastWasInvalid == false) {
+				lastWasInvalid = true;
+
+				int hash = 0;
+				boolean resetLastInvalidOnParent = false; // Added in 3.7.3 to resolve a timing regression introduced in 3.7.1
+				for (ComponentWrapper wrapper : ccMap.keySet()) {
+					Object component = wrapper.getComponent();
+					if (component instanceof JTextArea || component instanceof JEditorPane)
+						resetLastInvalidOnParent = true;
+					hash ^= wrapper.getLayoutHashCode();
+					hash += 285134905;
+				}
+				if (resetLastInvalidOnParent)
+					resetLastInvalidOnParent(parent);
+
+				if (hash != lastHash) {
+					grid = null;
+					lastHash = hash;
+				}
+
+				Dimension ps = parent.getSize();
+				if (lastInvalidSize == null || !lastInvalidSize.equals(ps)) {
+					if (grid != null)
+						grid.invalidateContainerSize();
+					lastInvalidSize = ps;
+				}
+			}
+		} else {
+			lastWasInvalid = false;
+		}
+
+		ContainerWrapper par = checkParent(parent);
+
+		setDebug(par, getDebugMillis() > 0);
+
+		if (grid == null)
+			grid = new Grid(par, lc, rowSpecs, colSpecs, ccMap, callbackList);
+
+		dirty = false;
+	}
+
+	/**
+	 * @since 3.7.3
+	 */
+	private void resetLastInvalidOnParent(Container parent)
+	{
+		while (parent != null) {
+			LayoutManager layoutManager = parent.getLayout();
+			if (layoutManager instanceof MigLayout) {
+				((MigLayout) layoutManager).lastWasInvalid = false;
+			}
+			parent = parent.getParent();
+		}
+	}
+
+	private ContainerWrapper checkParent(Container parent)
+	{
+		if (parent == null)
+			return null;
+
+		if (cacheParentW == null || cacheParentW.getComponent() != parent)
+			cacheParentW = new SwingContainerWrapper(parent);
+
+		return cacheParentW;
+	}
+
+	private long lastSize = 0;
+
+	public void layoutContainer(final Container parent)
+	{
+		synchronized(parent.getTreeLock()) {
+			checkCache(parent);
+
+			Insets i = parent.getInsets();
+			int[] b = new int[] {
+					i.left,
+					i.top,
+					parent.getWidth() - i.left - i.right,
+					parent.getHeight() - i.top - i.bottom
+			};
+
+			if (grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug(), true)) {
+				grid = null;
+				checkCache(parent);
+				grid.layout(b, lc.getAlignX(), lc.getAlignY(), getDebug(), false);
+			}
+
+			long newSize = grid.getHeight()[1] + (((long) grid.getWidth()[1]) << 32);
+			if (lastSize != newSize) {
+				lastSize = newSize;
+				final ContainerWrapper containerWrapper = checkParent(parent);
+				Window win = ((Window) SwingUtilities.getAncestorOfClass(Window.class, (Component)containerWrapper.getComponent()));
+				if (win != null) {
+				   if (win.isVisible()) {
+					   SwingUtilities.invokeLater(new Runnable() {
+						   public void run() {
+							   adjustWindowSize(containerWrapper);
+						   }
+					   });
+				   } else {
+					   adjustWindowSize(containerWrapper);
+				   }
+				}
+			}
+			lastInvalidSize = null;
+		}
+	}
+
+	/** Checks the parent window if its size is within parameters as set by the LC.
+	 * @param parent The parent who's window to possibly adjust the size for.
+	 */
+	private void adjustWindowSize(ContainerWrapper parent)
+	{
+		BoundSize wBounds = lc.getPackWidth();
+		BoundSize hBounds = lc.getPackHeight();
+
+		if (wBounds == null && hBounds == null)
+			return;
+
+		Window win = ((Window) SwingUtilities.getAncestorOfClass(Window.class, (Component) parent.getComponent()));
+		if (win == null)
+			return;
+
+		Dimension prefSize = win.getPreferredSize();
+		int targW = constrain(checkParent(win), win.getWidth(), prefSize.width, wBounds);
+		int targH = constrain(checkParent(win), win.getHeight(), prefSize.height, hBounds);
+
+		int x = Math.round(win.getX() - ((targW - win.getWidth()) * (1 - lc.getPackWidthAlign())));
+		int y = Math.round(win.getY() - ((targH - win.getHeight()) * (1 - lc.getPackHeightAlign())));
+
+		win.setBounds(x, y, targW, targH);
+	}
+
+	private int constrain(ContainerWrapper parent, int winSize, int prefSize, BoundSize constrain)
+	{
+		if (constrain == null)
+			return winSize;
+
+		int retSize = winSize;
+		UnitValue wUV = constrain.getPreferred();
+		if (wUV != null)
+			retSize = wUV.getPixels(prefSize, parent, parent);
+
+		retSize = constrain.constrain(retSize, prefSize, parent);
+
+		return constrain.getGapPush() ? Math.max(winSize, retSize) : retSize;
+	}
+
+	public Dimension minimumLayoutSize(Container parent)
+	{
+		synchronized(parent.getTreeLock()) {
+			return getSizeImpl(parent, LayoutUtil.MIN);
+		}
+	}
+
+	public Dimension preferredLayoutSize(Container parent)
+	{
+		synchronized(parent.getTreeLock()) {
+           if (lastParentSize == null || !parent.getSize().equals(lastParentSize)) {
+               for (ComponentWrapper wrapper : ccMap.keySet()) {
+                   Component c = (Component) wrapper.getComponent();
+                   if (c instanceof JTextArea || c instanceof JEditorPane || (c instanceof JComponent && Boolean.TRUE.equals(((JComponent)c).getClientProperty("migLayout.dynamicAspectRatio")))) {
+                       layoutContainer(parent);
+                       break;
+                   }
+               }
+           }
+
+           lastParentSize = parent.getSize();
+           return getSizeImpl(parent, LayoutUtil.PREF);
+		}
+	}
+
+	public Dimension maximumLayoutSize(Container parent)
+	{
+		return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
+	}
+
+	// Implementation method that does the job.
+	private Dimension getSizeImpl(Container parent, int sizeType)
+	{
+		checkCache(parent);
+
+		Insets i = parent.getInsets();
+
+		int w = LayoutUtil.getSizeSafe(grid != null ? grid.getWidth() : null, sizeType) + i.left + i.right;
+		int h = LayoutUtil.getSizeSafe(grid != null ? grid.getHeight() : null, sizeType) + i.top + i.bottom;
+
+		return new Dimension(w, h);
+	}
+
+	public float getLayoutAlignmentX(Container parent)
+	{
+		return lc != null && lc.getAlignX() != null ? lc.getAlignX().getPixels(1, checkParent(parent), null) : 0;
+	}
+
+	public float getLayoutAlignmentY(Container parent)
+	{
+		return lc != null && lc.getAlignY() != null ? lc.getAlignY().getPixels(1, checkParent(parent), null) : 0;
+	}
+
+	public void addLayoutComponent(String s, Component comp)
+	{
+		addLayoutComponent(comp, s);
+	}
+
+	public void addLayoutComponent(Component comp, Object constraints)
+	{
+		synchronized(comp.getParent().getTreeLock()) {
+			setComponentConstraintsImpl(comp, constraints, true);
+		}
+	}
+
+	public void removeLayoutComponent(Component comp)
+	{
+		synchronized(comp.getParent().getTreeLock()) {
+			scrConstrMap.remove(comp);
+			ccMap.remove(new SwingComponentWrapper(comp));
+		}
+	}
+
+	public void invalidateLayout(Container target)
+	{
+//		if (lc.isNoCache())  // Commented for 3.5 since there was too often that the "nocache" was needed and the user did not know.
+		dirty = true;
+
+		// the validity of components is maintained automatically.
+	}
+
+	// ************************************************
+	// Persistence Delegate and Serializable combined.
+	// ************************************************
+
+	private Object readResolve() throws ObjectStreamException
+	{
+		return LayoutUtil.getSerializedObject(this);
+	}
+
+	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+	{
+		LayoutUtil.setSerializedObject(this, LayoutUtil.readAsXML(in));
+	}
+
+	public void writeExternal(ObjectOutput out) throws IOException
+	{
+		if (getClass() == MigLayout.class)
+			LayoutUtil.writeAsXML(out, this);
+	}
+
+	private class MyDebugRepaintListener implements ActionListener
+	{
+		public void actionPerformed(ActionEvent e)
+		{
+			if (grid != null) {
+				Component comp = (Component) grid.getContainer().getComponent();
+				if (comp.isShowing()) {
+					grid.paintDebug();
+					return;
+				}
+			}
+			debugTimer.stop();
+			debugTimer = null;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/net/miginfocom/swing/SwingComponentWrapper.java b/src/net/miginfocom/swing/SwingComponentWrapper.java
new file mode 100644
index 0000000..1b7e4e5
--- /dev/null
+++ b/src/net/miginfocom/swing/SwingComponentWrapper.java
@@ -0,0 +1,459 @@
+package net.miginfocom.swing;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+import net.miginfocom.layout.ComponentWrapper;
+import net.miginfocom.layout.ContainerWrapper;
+import net.miginfocom.layout.PlatformDefaults;
+
+import javax.swing.*;
+import javax.swing.text.JTextComponent;
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.lang.reflect.Method;
+import java.util.IdentityHashMap;
+
+/**
+ */
+public class SwingComponentWrapper implements ComponentWrapper
+{
+	private static boolean maxSet = false;
+
+	private static boolean vp = true;
+
+	/** Debug color for component bounds outline.
+	 */
+	private static final Color DB_COMP_OUTLINE = new Color(0, 0, 200);
+
+	private final Component c;
+	private int compType = TYPE_UNSET;
+	private Boolean bl = null;
+	private boolean prefCalled = false;
+
+	public SwingComponentWrapper(Component c)
+	{
+		this.c = c;
+	}
+
+	public final int getBaseline(int width, int height)
+	{
+		if (BL_METHOD == null)
+			return -1;
+
+		try {
+			Object[] args = new Object[] {
+				width < 0 ? c.getWidth() : width,
+				height < 0 ? c.getHeight() : height
+			};
+
+			return (Integer) BL_METHOD.invoke(c, args);
+		} catch (Exception e) {
+			return -1;
+		}
+	}
+
+	public final Object getComponent()
+	{
+		return c;
+	}
+
+	/** Cache.
+	 */
+	private final static IdentityHashMap<FontMetrics, Point.Float> FM_MAP = new IdentityHashMap<FontMetrics, Point.Float>(4);
+	private final static Font SUBST_FONT = new Font("sansserif", Font.PLAIN, 11);
+
+	public final float getPixelUnitFactor(boolean isHor)
+	{
+		switch (PlatformDefaults.getLogicalPixelBase()) {
+			case PlatformDefaults.BASE_FONT_SIZE:
+				Font font = c.getFont();
+				FontMetrics fm = c.getFontMetrics(font != null ? font : SUBST_FONT);
+				Point.Float p = FM_MAP.get(fm);
+				if (p == null) {
+					Rectangle2D r = fm.getStringBounds("X", c.getGraphics());
+					p = new Point.Float(((float) r.getWidth()) / 6f, ((float) r.getHeight()) / 13.27734375f);
+					FM_MAP.put(fm, p);
+				}
+				return isHor ? p.x : p.y;
+
+			case PlatformDefaults.BASE_SCALE_FACTOR:
+
+				Float s = isHor ? PlatformDefaults.getHorizontalScaleFactor() : PlatformDefaults.getVerticalScaleFactor();
+				if (s != null)
+					return s;
+				return (isHor ? getHorizontalScreenDPI() : getVerticalScreenDPI()) / (float) PlatformDefaults.getDefaultDPI();
+
+			default:
+				return 1f;
+		}
+	}
+
+//	/** Cache.
+//	 */
+//	private final static IdentityHashMap<FontMetrics, Point.Float> FM_MAP2 = new IdentityHashMap<FontMetrics, Point.Float>(4);
+//	private final static Font SUBST_FONT2 = new Font("sansserif", Font.PLAIN, 11);
+//
+//	public float getDialogUnit(boolean isHor)
+//	{
+//		Font font = c.getFont();
+//		FontMetrics fm = c.getFontMetrics(font != null ? font : SUBST_FONT2);
+//		Point.Float dluP = FM_MAP2.get(fm);
+//		if (dluP == null) {
+//			float w = fm.charWidth('X') / 4f;
+//			int ascent = fm.getAscent();
+//			float h = (ascent > 14 ? ascent : ascent + (15 - ascent) / 3) / 8f;
+//
+//			dluP = new Point.Float(w, h);
+//			FM_MAP2.put(fm, dluP);
+//		}
+//		return isHor ? dluP.x : dluP.y;
+//	}
+
+	public final int getX()
+	{
+		return c.getX();
+	}
+
+	public final int getY()
+	{
+		return c.getY();
+	}
+
+	public final int getHeight()
+	{
+		return c.getHeight();
+	}
+
+	public final int getWidth()
+	{
+		return c.getWidth();
+	}
+
+	public final int getScreenLocationX()
+	{
+		Point p = new Point();
+		SwingUtilities.convertPointToScreen(p, c);
+		return p.x;
+	}
+
+	public final int getScreenLocationY()
+	{
+		Point p = new Point();
+		SwingUtilities.convertPointToScreen(p, c);
+		return p.y;
+	}
+
+	public final int getMinimumHeight(int sz)
+	{
+		if (prefCalled == false) {
+			c.getPreferredSize(); // To defeat a bug where the minimum size is different before and after the first call to getPreferredSize();
+			prefCalled = true;
+		}
+		return c.getMinimumSize().height;
+	}
+
+	public final int getMinimumWidth(int sz)
+	{
+		if (prefCalled == false) {
+			c.getPreferredSize(); // To defeat a bug where the minimum size is different before and after the first call to getPreferredSize();
+			prefCalled = true;
+		}
+		return c.getMinimumSize().width;
+	}
+	public final int getPreferredHeight(int sz)
+	{
+		// If the component has not gotten size yet and there is a size hint, trick Swing to return a better height.
+		if (c.getWidth() == 0 && c.getHeight() == 0 && sz != -1)
+			c.setBounds(c.getX(), c.getY(), sz, 1);
+
+		return c.getPreferredSize().height;
+	}
+
+	public final int getPreferredWidth(int sz)
+	{
+		// If the component has not gotten size yet and there is a size hint, trick Swing to return a better height.
+		if (c.getWidth() == 0 && c.getHeight() == 0 && sz != -1)
+			c.setBounds(c.getX(), c.getY(), 1, sz);
+
+		return c.getPreferredSize().width;
+	}
+
+	public final int getMaximumHeight(int sz)
+	{
+		if (!isMaxSet(c))
+			return Short.MAX_VALUE;
+
+		return c.getMaximumSize().height;
+	}
+
+	public final int getMaximumWidth(int sz)
+	{
+		if (!isMaxSet(c))
+			return Short.MAX_VALUE;
+
+		return c.getMaximumSize().width;
+	}
+
+
+	private boolean isMaxSet(Component c)
+	{
+		if (IMS_METHOD != null) {
+			try {
+				return (Boolean) IMS_METHOD.invoke(c, (Object[]) null);
+			} catch (Exception e) {
+				IMS_METHOD = null;  // So we do not try every time.
+			}
+		}
+		return isMaxSizeSetOn1_4();
+	}
+
+	public final ContainerWrapper getParent()
+	{
+		Container p = c.getParent();
+		return p != null ? new SwingContainerWrapper(p) : null;
+	}
+
+	public final int getHorizontalScreenDPI()
+	{
+		return PlatformDefaults.getDefaultDPI();
+	}
+
+	public final int getVerticalScreenDPI()
+	{
+		return PlatformDefaults.getDefaultDPI();
+	}
+
+	public final int getScreenWidth()
+	{
+		try {
+			return c.getToolkit().getScreenSize().width;
+		} catch (HeadlessException ex) {
+			return 1024;
+		}
+	}
+
+	public final int getScreenHeight()
+	{
+		try {
+			return c.getToolkit().getScreenSize().height;
+		} catch (HeadlessException ex) {
+			return 768;
+		}
+	}
+
+	public final boolean hasBaseline()
+	{
+		if (bl == null) {
+			try {
+				if (BL_RES_METHOD == null || BL_RES_METHOD.invoke(c).toString().equals("OTHER")) {
+					bl = Boolean.FALSE;
+				} else {
+					Dimension d = c.getMinimumSize();
+					bl = getBaseline(d.width, d.height) > -1;
+				}
+			} catch (Throwable ex) {
+				bl = Boolean.FALSE;
+			}
+		}
+		return bl;
+	}
+
+	public final String getLinkId()
+	{
+		return c.getName();
+	}
+
+	public final void setBounds(int x, int y, int width, int height)
+	{
+		c.setBounds(x, y, width, height);
+	}
+
+	public boolean isVisible()
+	{
+		return c.isVisible();
+	}
+
+	public final int[] getVisualPadding()
+	{
+		if (vp && c instanceof JTabbedPane) {
+			if (UIManager.getLookAndFeel().getClass().getName().endsWith("WindowsLookAndFeel"))
+				return new int[] {-1, 0, 2, 2};
+		}
+
+		return null;
+	}
+
+	public static boolean isMaxSizeSetOn1_4()
+	{
+		return maxSet;
+	}
+
+	public static void setMaxSizeSetOn1_4(boolean b)
+	{
+		maxSet = b;
+	}
+
+	public static boolean isVisualPaddingEnabled()
+	{
+		return vp;
+	}
+
+	public static void setVisualPaddingEnabled(boolean b)
+	{
+		vp = b;
+	}
+
+	public final void paintDebugOutline()
+	{
+		if (c.isShowing() == false)
+			return;
+
+		Graphics2D g = (Graphics2D) c.getGraphics();
+		if (g == null)
+			return;
+
+		g.setPaint(DB_COMP_OUTLINE);
+		g.setStroke(new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {2f, 4f}, 0));
+		g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
+	}
+
+	public int getComponetType(boolean disregardScrollPane)
+	{
+		if (compType == TYPE_UNSET)
+			compType = checkType(disregardScrollPane);
+
+		return compType;
+	}
+
+	public int getLayoutHashCode()
+	{
+		Dimension d = c.getMaximumSize();
+		int hash = d.width + (d.height << 5);
+
+		d = c.getPreferredSize();
+		hash += (d.width << 10) + (d.height << 15);
+
+		d = c.getMinimumSize();
+		hash += (d.width << 20) + (d.height << 25);
+
+		if (c.isVisible())
+			hash += 1324511;
+
+		String id = getLinkId();
+		if (id != null)
+			hash += id.hashCode();
+
+		return hash;
+	}
+
+	private int checkType(boolean disregardScrollPane)
+	{
+		Component c = this.c;
+
+		if (disregardScrollPane) {
+			if (c instanceof JScrollPane) {
+				c = ((JScrollPane) c).getViewport().getView();
+			} else if (c instanceof ScrollPane) {
+				c = ((ScrollPane) c).getComponent(0);
+			}
+		}
+
+		if (c instanceof JTextField || c instanceof TextField) {
+			return TYPE_TEXT_FIELD;
+		} else if (c instanceof JLabel || c instanceof Label) {
+			return TYPE_LABEL;
+		} else if (c instanceof JToggleButton || c instanceof Checkbox) {
+			return TYPE_CHECK_BOX;
+		} else if (c instanceof AbstractButton || c instanceof Button) {
+			return TYPE_BUTTON;
+		} else if (c instanceof JComboBox || c instanceof Choice) {
+			return TYPE_LABEL;
+		} else if (c instanceof JTextComponent || c instanceof TextComponent) {
+			return TYPE_TEXT_AREA;
+		} else if (c instanceof JPanel || c instanceof Canvas) {
+			return TYPE_PANEL;
+		} else if (c instanceof JList || c instanceof List) {
+			return TYPE_LIST;
+		} else if (c instanceof JTable) {
+			return TYPE_TABLE;
+		} else if (c instanceof JSeparator) {
+			return TYPE_SEPARATOR;
+		} else if (c instanceof JSpinner) {
+			return TYPE_SPINNER;
+		} else if (c instanceof JProgressBar) {
+			return TYPE_PROGRESS_BAR;
+		} else if (c instanceof JSlider) {
+			return TYPE_SLIDER;
+		} else if (c instanceof JScrollPane) {
+			return TYPE_SCROLL_PANE;
+		} else if (c instanceof JScrollBar || c instanceof Scrollbar) {
+			return TYPE_SCROLL_BAR;
+		} else if (c instanceof Container) {    // only AWT components is not containers.
+			return TYPE_CONTAINER;
+		}
+		return TYPE_UNKNOWN;
+	}
+
+	public final int hashCode()
+	{
+		return getComponent().hashCode();
+	}
+
+	public final boolean equals(Object o)
+	{
+		if (o instanceof ComponentWrapper == false)
+			return false;
+
+		return getComponent().equals(((ComponentWrapper) o).getComponent());
+	}
+
+	/** Cached method used for getting base line with reflection.
+	 */
+	private static Method BL_METHOD = null;
+	private static Method BL_RES_METHOD = null;
+	static {
+		try {
+			BL_METHOD = Component.class.getDeclaredMethod("getBaseline", new Class[] {int.class, int.class});
+			BL_RES_METHOD = Component.class.getDeclaredMethod("getBaselineResizeBehavior"); // 3.7.2: Removed Class<?> null since that made the method inaccessible.
+		} catch (Throwable e) { // No such method or security exception
+		}
+	}
+
+	private static Method IMS_METHOD = null;
+	static {
+		try {
+			IMS_METHOD = Component.class.getDeclaredMethod("isMaximumSizeSet", (Class[]) null);
+		} catch (Throwable e) { // No such method or security exception
+		}
+	}
+}
diff --git a/src/net/miginfocom/swing/SwingContainerWrapper.java b/src/net/miginfocom/swing/SwingContainerWrapper.java
new file mode 100644
index 0000000..9cbe39f
--- /dev/null
+++ b/src/net/miginfocom/swing/SwingContainerWrapper.java
@@ -0,0 +1,110 @@
+package net.miginfocom.swing;
+/*
+ * License (BSD):
+ * ==============
+ *
+ * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ * Neither the name of the MiG InfoCom AB nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * @version 1.0
+ * @author Mikael Grev, MiG InfoCom AB
+ *         Date: 2006-sep-08
+ */
+
+import net.miginfocom.layout.ComponentWrapper;
+import net.miginfocom.layout.ContainerWrapper;
+
+import java.awt.*;
+
+/**
+ */
+public final class SwingContainerWrapper extends SwingComponentWrapper implements ContainerWrapper
+{
+	/** Debug color for cell outline.
+	 */
+	private static final Color DB_CELL_OUTLINE = new Color(255, 0, 0);
+
+	public SwingContainerWrapper(Container c)
+	{
+		super(c);
+	}
+
+	public ComponentWrapper[] getComponents()
+	{
+		Container c = (Container) getComponent();
+		ComponentWrapper[] cws = new ComponentWrapper[c.getComponentCount()];
+		for (int i = 0; i < cws.length; i++)
+			cws[i] = new SwingComponentWrapper(c.getComponent(i));
+		return cws;
+	}
+
+	public int getComponentCount()
+	{
+		return ((Container) getComponent()).getComponentCount();
+	}
+
+	public Object getLayout()
+	{
+		return ((Container) getComponent()).getLayout();
+	}
+
+	public final boolean isLeftToRight()
+	{
+		return ((Container) getComponent()).getComponentOrientation().isLeftToRight();
+	}
+
+	public final void paintDebugCell(int x, int y, int width, int height)
+	{
+		Component c = (Component) getComponent();
+		if (c.isShowing() == false)
+			return;
+
+		Graphics2D g = (Graphics2D) c.getGraphics();
+		if (g == null)
+			return;
+
+		g.setStroke(new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {2f, 3f}, 0));
+		g.setPaint(DB_CELL_OUTLINE);
+		g.drawRect(x, y, width - 1, height - 1);
+	}
+
+	public int getComponetType(boolean disregardScrollPane)
+	{
+		return TYPE_CONTAINER;
+	}
+
+	// Removed for 2.3 because the parent.isValid() in MigLayout will catch this instead.
+	@SuppressWarnings("unused")
+	public int getLayoutHashCode()
+	{
+		long n = System.nanoTime();
+		int h = super.getLayoutHashCode();
+
+		if (isLeftToRight())
+			h += 416343;
+
+		return 0;
+	}
+}
diff --git a/src/netscape/javascript/JSException.class b/src/netscape/javascript/JSException.class
deleted file mode 100644
index 4d3f1d2..0000000
Binary files a/src/netscape/javascript/JSException.class and /dev/null differ
diff --git a/src/netscape/javascript/JSObject.class b/src/netscape/javascript/JSObject.class
deleted file mode 100644
index 8c24460..0000000
Binary files a/src/netscape/javascript/JSObject.class and /dev/null differ
diff --git a/src/netscape/javascript/JSUtil.class b/src/netscape/javascript/JSUtil.class
deleted file mode 100644
index 6395261..0000000
Binary files a/src/netscape/javascript/JSUtil.class and /dev/null differ
diff --git a/src/netscape/security/ForbiddenTargetException.class b/src/netscape/security/ForbiddenTargetException.class
deleted file mode 100644
index 9599a3f..0000000
Binary files a/src/netscape/security/ForbiddenTargetException.class and /dev/null differ
diff --git a/src/netscape/security/ParameterizedTarget.class b/src/netscape/security/ParameterizedTarget.class
deleted file mode 100644
index 255c200..0000000
Binary files a/src/netscape/security/ParameterizedTarget.class and /dev/null differ
diff --git a/src/netscape/security/Principal.class b/src/netscape/security/Principal.class
deleted file mode 100644
index 0861884..0000000
Binary files a/src/netscape/security/Principal.class and /dev/null differ
diff --git a/src/netscape/security/Privilege.class b/src/netscape/security/Privilege.class
deleted file mode 100644
index cbfd234..0000000
Binary files a/src/netscape/security/Privilege.class and /dev/null differ
diff --git a/src/netscape/security/PrivilegeManager.class b/src/netscape/security/PrivilegeManager.class
deleted file mode 100644
index 0961599..0000000
Binary files a/src/netscape/security/PrivilegeManager.class and /dev/null differ
diff --git a/src/netscape/security/PrivilegeTable.class b/src/netscape/security/PrivilegeTable.class
deleted file mode 100644
index fe4b12f..0000000
Binary files a/src/netscape/security/PrivilegeTable.class and /dev/null differ
diff --git a/src/netscape/security/Target.class b/src/netscape/security/Target.class
deleted file mode 100644
index 1c13ef5..0000000
Binary files a/src/netscape/security/Target.class and /dev/null differ
diff --git a/src/netscape/security/UserDialogHelper.class b/src/netscape/security/UserDialogHelper.class
deleted file mode 100644
index 465a88c..0000000
Binary files a/src/netscape/security/UserDialogHelper.class and /dev/null differ
diff --git a/src/netscape/security/UserTarget.class b/src/netscape/security/UserTarget.class
deleted file mode 100644
index a4adccc..0000000
Binary files a/src/netscape/security/UserTarget.class and /dev/null differ
diff --git a/src/opticalraytracer/Beep.java b/src/opticalraytracer/Beep.java
new file mode 100644
index 0000000..241a134
--- /dev/null
+++ b/src/opticalraytracer/Beep.java
@@ -0,0 +1,76 @@
+package opticalraytracer;
+
+import javax.sound.sampled.*;
+
+final public class Beep extends Thread {
+
+    float sampleRate = 32000;
+    int freqHz;
+    int durationMsec;
+    double level;
+
+    // 0 <= level <= 1.0
+    public Beep(int freqHz, int durationMsec, double level) {
+        this.freqHz = freqHz;
+        this.durationMsec = durationMsec;
+        this.level = level * 32767;
+    }
+
+    private double envelope(double a, double b, double t, double tc) {
+        return ((b - t) * (-a + t)) / ((b - t + tc) * (-a + t + tc));
+    }
+
+    public void run() {
+        try {
+            int bsize = (int) (2 * sampleRate * durationMsec / 1000);
+            byte[] buf = new byte[bsize];
+            double step = 2.0 * Math.PI * freqHz / sampleRate;
+            double angle = 0;
+            int i = 0;
+            while (i < bsize) {
+                int n = (int) (Math.sin(angle) * level * envelope(0, bsize, i, 1000));
+                buf[i++] = (byte) (n % 256);
+                buf[i++] = (byte) (n / 256);
+                angle += step;
+            }
+
+            AudioFormat af = new AudioFormat(sampleRate, 16, 1, true, false);
+            SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
+            sdl.open(af);
+            sdl.start();
+            sdl.write(buf, 0, buf.length);
+            Thread.sleep(durationMsec * 2);
+            sdl.drain();
+            sdl.close();
+            //System.out.println("DONE");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void beep(double level) {
+        Beep b = new Beep(1000, 100, level);
+        b.start();
+        // in standalone mode, must join thread
+        // to prevent an ugly artifact
+        try {
+            b.join();
+        } catch (Exception e) {
+        }
+    }
+
+    public static void beep() {
+        Beep b = new Beep(1000, 100, .5);
+        b.start();
+        // in standalone mode, must join thread
+        // to prevent an ugly artifact
+        try {
+            b.join();
+        } catch (Exception e) {
+        }
+    }
+
+    public static void main(String[] args) {
+        beep(.5);
+    }
+}
diff --git a/src/opticalraytracer/ColorButton.java b/src/opticalraytracer/ColorButton.java
index 19b8734..24e95dc 100644
--- a/src/opticalraytracer/ColorButton.java
+++ b/src/opticalraytracer/ColorButton.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,47 +17,50 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
+
 package opticalraytracer;
 
 import java.awt.*;
 import java.awt.event.*;
+
 import javax.swing.*;
 
-/**
- *
- * @author lutusp
- */
-public class ColorButton {
+ at SuppressWarnings("serial")
+final public class ColorButton extends JButton implements ProgramControl {
 
     Color col;
-    JButton button;
+    private String tag = "";
     OpticalRayTracer parent;
 
-    public ColorButton(OpticalRayTracer p, JButton button, Color col, String tip) {
-        this.col = col;
-        init(p, button, tip);
+    public ColorButton(OpticalRayTracer p, String tag, String tip) {
+        init(p, tag,tip);
     }
 
-    public ColorButton(OpticalRayTracer p, JButton button, String s, String tip) {
+    public ColorButton(OpticalRayTracer p, String s, String tag, String tip) {
         this.col = new Color(Integer.parseInt(s));
-        init(p, button, tip);
+        init(p, tag,tip);
     }
 
-    public ColorButton(OpticalRayTracer p, JButton button, int c, String tip) {
+    public ColorButton(OpticalRayTracer p, int c, String tag, String tip) {
         this.col = new Color(c);
-        init(p, button, tip);
+        init(p, tag,tip);
+    }
+    
+    public String getTag() {
+    	return tag;
     }
 
-    void init(OpticalRayTracer p, JButton button, String tip) {
+    void init(OpticalRayTracer p, String tag, String tip) {
         this.parent = p;
-        this.button = button;
-        button.setToolTipText(tip);
+        this.tag = tag;
+        setValue(parent.programValues.getOneValue(tag));
+        setToolTipText(tip);
+        Dimension d = new Dimension(48,32);
+        this.setMaximumSize(d);
+        this.setMinimumSize(d);
+        this.setPreferredSize(d);
 
-        button.addMouseListener(new java.awt.event.MouseAdapter() {
+        addMouseListener(new java.awt.event.MouseAdapter() {
 
             @Override
             public void mouseClicked(java.awt.event.MouseEvent evt) {
@@ -66,36 +69,50 @@ public class ColorButton {
         });
         paintButton();
     }
+    
+    public void reset() {
+    	int cc = Integer.parseInt(parent.programValues.getOneValue(tag));
+    	col = new MyColor(cc);
+    	paintButton();
+    }
 
     // all this because you can't define colors
     // for Swing buttons
     void paintButton() {
         int rgb = col.getRGB();
-        String hex = Integer.toHexString(rgb);
-        if (hex.length() == 8) {
-            hex = hex.substring(2, 8);
-        }
+        String hex = String.format("%06x",rgb & 0xffffff);
         String str = "<html><span style=\"background:#" +
-                hex + ";\">       </html>";
-        button.setText(str);
+                hex + ";\">    </html>";
+        setText(str);
         parent.updateGraphicDisplay();
     }
 
     void handleMouseClicked(MouseEvent evt) {
-        Color cc = JColorChooser.showDialog(parent, "Choose a color", col);
+        Color cc = JColorChooser.showDialog(parent.frame, "Choose a color", col);
         if (cc != null) {
             col = cc;
+            parent.undoPush();
+            parent.programValues.setOneValue(tag,getValue());
             paintButton();
         }
     }
+    
+    public String getValue() {
+    	int ic = col.getBlue() & 0xff;
+        ic |= (col.getGreen() & 0xff) << 8;
+        ic |= (col.getRed() & 0xff) << 16;
+        ic |= (col.getAlpha() & 0xff) << 24;
+        return "" + ic;
+    }
 
     @Override
     public String toString() {
-        return "" + col.getRGB();
+        return getValue();
     }
 
-    public void setColor(String s) {
-        col = new Color(Integer.parseInt(s));
+    public void setValue(String s) {
+    	int cc = LocaleHandler.getInt(s);
+        col = new MyColor(cc);
         paintButton();
     }
 
diff --git a/src/opticalraytracer/ColorPos.java b/src/opticalraytracer/ColorPos.java
deleted file mode 100644
index 4748713..0000000
--- a/src/opticalraytracer/ColorPos.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-/**
- *
- * @author lutusp
- */
-final public class ColorPos {
-
-    double r;
-    double g;
-    double b;
-    double p;
-    MyColor col;
-
-    public ColorPos(double r, double g, double b, double p) {
-        this.r = r;
-        this.g = g;
-        this.b = b;
-        col = new MyColor(r, g, b);
-        this.p = p;
-    }
-}
diff --git a/src/opticalraytracer/Common.java b/src/opticalraytracer/Common.java
new file mode 100644
index 0000000..890fd94
--- /dev/null
+++ b/src/opticalraytracer/Common.java
@@ -0,0 +1,222 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.*;
+
+import java.util.Date;
+
+final public class Common {
+	static final double radians = PI / 180;
+	static final double degrees = 180 / PI;
+	static final int OBJECT_REFRACTOR = 0;
+	static final int OBJECT_REFLECTOR = 1;
+	static final int OBJECT_ABSORBER = 2;
+	static final String[] opticalTypes = new String[] { "Lens", "Mirror",
+			"Absorber" };
+	
+	static final int CURVATURE_SPHERICAL = 0;
+	static final int CURVATURE_PARABOLIC = 1;
+	static final int CURVATURE_HYPERBOLIC = 2;
+	static final int CURVATURE_PLANAR = 3;
+	static final String[] curvatures = new String[] {"Spherical", "Parabolic",
+			"Hyperbolic", "Planar" };
+	static final int TAB_DESIGN = 0;
+	static final int TAB_CONFIGURE = 1;
+	static final int TAB_TABLE = 2;
+	static final int TAB_HELP = 3;
+
+	static int noFocusHiInt = 0xf0f0f0;
+	static int noFocusLoInt = 0xa0a0a0;
+	static int noFocusInverseInt = 0x202020;
+	static MyColor noFocusHi = new MyColor(noFocusHiInt);
+	static MyColor noFocusLo = new MyColor(noFocusLoInt);
+	static MyColor noFocusInverse = new MyColor(noFocusInverseInt);
+
+	final static void p(String s) {
+		s = s.replaceAll("(\\.)(\\d{5})\\d+", "$1$2");
+		Date d = new Date();
+		System.out.println(d + " : " + s);
+	}
+
+	final static String pd(double v) {
+		return pf(v * degrees);
+
+	}
+
+	final static String pf(double v) {
+		return String.format("%8.4f", v);
+
+	}
+
+	final static String pb(boolean v) {
+		return String.format("%5s", "" + v);
+
+	}
+
+	final static void pr(String s) {
+		s = s.replaceAll("(\\.)(\\d{3})\\d+","$1$2");
+		System.out.println(s);
+	}
+
+	final static String getObjectType(int function) {
+		return opticalTypes[function];
+	}
+
+	// reflection in a plane mirror, angles radians
+	
+	// http://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector
+
+	final static Vector computeReflectionAngle(Vector via, Vector vsa) {
+		return via.sub(vsa.mul(via.dot(vsa)).mul(2));
+	}
+
+	final static double ntrp(double x, double xa, double xb, double ya,
+			double yb) {
+		return ((x - xa) / (xb - xa)) * (yb - ya) + ya;
+	}
+
+	final static void beep() {
+		Beep.beep();
+	}
+
+	// is a given point within the bound formed by
+	// line segment (x1,y1) -> (x2,y2)?
+	final static boolean inBounds(double x, double y, double x1, double y1,
+			double x2, double y2) {
+		if (x1 != x2) {
+			return ((x1 <= x && x <= x2) || (x2 <= x && x <= x1));
+		} else {
+			return ((y1 <= y && y <= y2) || (y2 <= y && y <= y1));
+		}
+	}
+
+	final static String wrapTag(String tag, String data, String also,
+			boolean linefeeds) {
+		if (linefeeds) {
+			return String.format("<%s %s>\n%s\n</%s>\n", tag, also, data, tag);
+		} else {
+			return String.format("<%s %s>%s</%s>", tag, also, data, tag);
+		}
+	}
+
+	final static double sign(double v) {
+		return (v < 0) ? -1 : 1;
+	}
+
+	final static double distanceToLine(double x, double y, double x1,
+			double y1, double x2, double y2) {
+		// see Sage "OpticalRayTracer_technical_article" worksheet
+		return abs(-(x1 - x2) * y + x * (y1 - y2) - x2 * y1 + x1 * y2)
+				/ sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
+	}
+
+	final static double xCoordinateOnLine(double x, double y, double x1,
+			double y1, double x2, double y2) {
+		// see Sage "OpticalRayTracer_technical_article" worksheet
+		return ((x * (x1 - x2) + y * (y1 - y2)) * (x1 - x2) + (x2 * y1 - x1
+				* y2)
+				* (y1 - y2))
+				/ (pow(x1 - x2, 2) + pow(y1 - y2, 2));
+	}
+
+	final static double yCoordinateOnLine(double x, double y, double x1,
+			double y1, double x2, double y2) {
+		// see Sage "OpticalRayTracer_technical_article" worksheet
+		return -((x2 * y1 - x1 * y2) * (x1 - x2) - (x * (x1 - x2) + y
+				* (y1 - y2))
+				* (y1 - y2))
+				/ (pow(x1 - x2, 2) + pow(y1 - y2, 2));
+	}
+
+	// (x1,y1) is an arbitrarily distant line beginning
+	// (x2,y2) is the point of interest
+	// (x1,y1) -> (x2,y2) is line segment to be tested
+	// (x3,y3) -> (x4,y4) is lens perimeter segment under test
+	// see Sage worksheet "OpticalRayTracer_technical_article"
+	// for the derivation
+	final static int linesIntersect(double x1, double y1, double x2, double y2,
+			double x3, double y3, double x4, double y4) {
+		double divisor = ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4));
+		if (divisor == 0 || Double.isNaN(divisor)) {
+			return -1;
+		}
+		double x = -((x4 * y3 - x3 * y4) * (x1 - x2) - (x2 * y1 - x1 * y2)
+				* (x3 - x4))
+				/ divisor;
+		double y = -((x4 * y3 - x3 * y4) * (y1 - y2) - (x2 * y1 - x1 * y2)
+				* (y3 - y4))
+				/ divisor;
+		if (Double.isNaN(x) || Double.isNaN(y)) {
+			// parent.p("NaN detected in \"inside\" test.");
+			return -1;
+		}
+		return Common.inBounds(x, y, x1, y1, x2, y2)
+				&& Common.inBounds(x, y, x3, y3, x4, y4) ? 1 : 0;
+	}
+
+	final static double distanceToParallelLine(Vector a, Vector b, Vector p) {
+		double lx = xCoordinateOnLine(p.x, p.y, a.x, a.y, b.x, b.y);
+		double ly = yCoordinateOnLine(p.x, p.y, a.x, a.y, b.x, b.y);
+		if (inBounds(lx, ly, a.x, a.y, b.x, b.y)) {
+			return distanceToLine(p.x, p.y, a.x, a.y, b.x, b.y);
+		} else {
+			return Double.NaN;
+		}
+	}
+
+	// a1 = incident angle (a2 assumed = 0) radians
+	// n1 = incident IOR
+	// n2 = media IOR
+	final static double snell(double a1, double n1, double n2) {
+		return asin(sin(a1) * n1 / n2);
+	}
+	
+	// http://www.cs.cmu.edu/~ph/refr.ps.gz ("derivation of refraction methods")
+
+	// Heckbert, shown on page 4, likely the most efficient of those tested:
+
+	// a1 = incident angle radians
+	// a2 = surface normal radians
+	// n1 = incident IOR
+	// n2 = media IOR
+	
+	// scalar form
+	final static double snell2d(double a1, double a2, double n1, double n2) {
+		Vector i = Vector.polar(a1);
+		Vector n = Vector.polar(-1,a2);
+		return snell2d(i,n,n1,n2).angle();
+	}
+	
+	// vector form
+	final static Vector snell2d(Vector i, Vector n, double n1, double n2) {
+		double c1 = -n.dot(i);
+		// this sign change eliminates much
+		// hacky code in the main program
+		if (c1 < 0) {
+			c1 = -c1;
+			n = n.negate();
+		}
+		double r = n1 / n2;
+		double c2 = sqrt(1 - r * r * (1 - c1 * c1));
+		return (i.mul(r)).add(n.mul(r * c1 - c2));
+	}
+}
diff --git a/src/opticalraytracer/ComplexInt.java b/src/opticalraytracer/ComplexInt.java
new file mode 100644
index 0000000..435edb9
--- /dev/null
+++ b/src/opticalraytracer/ComplexInt.java
@@ -0,0 +1,28 @@
+package opticalraytracer;
+
+public class ComplexInt {
+	int x,y;
+    public ComplexInt(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+    public ComplexInt(ComplexInt p) {
+    	x = p.x;
+    	y = p.y;
+    }
+    public ComplexInt() {
+    }
+    public void scale(double s) {
+    	x = (int) (x*s);
+    	y = (int) (y*s);
+    }
+    
+    public void assign(ComplexInt p) {
+    	x = p.x;
+    	y = p.y;
+    }
+    
+    public String toString() {
+    	return String.format("{%d,%d}",x,y);
+    }
+}
diff --git a/src/opticalraytracer/ControlManager.java b/src/opticalraytracer/ControlManager.java
new file mode 100644
index 0000000..2803aa5
--- /dev/null
+++ b/src/opticalraytracer/ControlManager.java
@@ -0,0 +1,364 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseWheelEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+final public class ControlManager implements ProgramControl {
+
+	JTextField numberField = null;
+	JTextField nameField = null;
+	JCheckBox box = null;
+	JRadioButton radio;
+	JComboBox<String> comboBox = null;
+	ArrayList<JRadioButton> radioButtonArray;
+	static int[] radioButtonList = new int[] { -1, -1, -1 };
+	int radioButtonIndex = -1;
+	OpticalRayTracer parent;
+	double sens = 1;
+	double dmin, dmax;
+	int imin, imax;
+	private String tag = "";
+
+	public ControlManager(JTextField field, OpticalRayTracer p, String tag) {
+		nameField = field;
+		init(p, tag);
+		assignHandlers(field);
+		nameField.addKeyListener(new java.awt.event.KeyAdapter() {
+			@Override
+			public void keyReleased(java.awt.event.KeyEvent evt) {
+				int kcode = evt.getKeyCode();
+				// Common.p("" + evt);
+				if (kcode == KeyEvent.VK_ENTER) {
+					evt.consume();
+					updateAllControls();
+				}
+			}
+		});
+		nameField.addFocusListener(new FocusAdapter() {
+			@Override
+			public void focusLost(FocusEvent e) {
+				updateAllControls();
+			}
+		});
+	}
+
+	public ControlManager(double sens, double min, double max,
+			JTextField field, OpticalRayTracer p, String tag) {
+		this.numberField = field;
+		dmin = min;
+		dmax = max;
+		this.sens = sens;
+		init(p, tag);
+		assignHandlers(field);
+	}
+
+	public ControlManager(final JCheckBox box, OpticalRayTracer p,
+			final String tag) {
+		this.box = box;
+		init(p, tag);
+		box.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				// TODO Auto-generated method stub
+				updateAllControls();
+			}
+		});
+	}
+
+	public ControlManager(final JRadioButton b, OpticalRayTracer p,
+			final String tag) {
+		radio = b;
+		init(p, tag);
+		int i = 0;
+		// choose which radio button this is
+		ArrayList<ArrayList<JRadioButton>> radioArrayList = new ArrayList<>(
+				Arrays.asList(parent.typeRadioButtonList));
+		for (ArrayList<JRadioButton> bl : radioArrayList) {
+			if (bl.indexOf(b) != -1) {
+				radioButtonIndex = i;
+				radioButtonArray = bl;
+			}
+			i++;
+		}
+		radio.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				radioButtonList[radioButtonIndex] = radioButtonArray
+						.indexOf(radio);
+				updateAllControls();
+			}
+		});
+	}
+
+	public ControlManager(JComboBox<String> cb, OpticalRayTracer p, String tag) {
+		comboBox = cb;
+		init(p, tag);
+		//comboBox.removeAll();
+		init(p, tag);
+		for (String s : Common.curvatures) {
+			comboBox.addItem(s);
+		}
+		comboBox.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				if (!parent.suppressCombo) {
+					updateAllControls();
+				}
+			}
+		});
+	}
+
+	public String getTag() {
+		return tag;
+	}
+
+	void init(OpticalRayTracer p, String tag) {
+		parent = p;
+		this.tag = tag;
+	}
+
+	public void reset() {
+	};
+
+	public void assignHandlers(Component comp) {
+		comp.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
+
+			public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
+				handleMouseWheelMoved(evt);
+			}
+		});
+		comp.addKeyListener(new java.awt.event.KeyAdapter() {
+
+			@Override
+			public void keyReleased(java.awt.event.KeyEvent evt) {
+				handleKeyPressed(evt);
+			}
+		});
+	}
+
+	void enable(boolean enabled) {
+		if (numberField != null) {
+			numberField.setEnabled(enabled);
+			if (!enabled) {
+				numberField.setText("(Select a lens)");
+			}
+		} else if (nameField != null) {
+			nameField.setEnabled(enabled);
+			if (!enabled) {
+				nameField.setText("");
+			}
+		} else if (box != null) {
+			box.setEnabled(enabled);
+		} else if (radio != null) {
+			for (JRadioButton jb : radioButtonArray) {
+				jb.setEnabled(enabled);
+			}
+		} else if (comboBox != null) {
+			comboBox.setEnabled(enabled);
+		}
+	}
+
+	public String getValue() {
+		String result = "";
+		if (numberField != null) {
+			result = numberField.getText();
+		} else if (nameField != null) {
+			result = nameField.getText();
+		} else if (box != null) {
+			result = "" + box.isSelected();
+		} else if (comboBox != null) {
+			result = "" + comboBox.getSelectedIndex();
+		} else {
+			result = "" + radioButtonList[radioButtonIndex];
+		}
+		// parent.p("controlmanager get value: tag: " + tag + ", value:" +
+		// result);
+		return result;
+	}
+
+	public void setValue(String s) {
+		// parent.p("controlmanager set value: tag: " + tag + ", value:" + s);
+		if (numberField != null) {
+			double v = 0;
+			try {
+				v = LocaleHandler.getDouble(s,
+						LocaleHandler.localeDecimalSeparator);
+			} catch (Exception e) {
+			}
+			numberField.setText(parent.formatNum(v));
+		} else if (nameField != null) {
+			nameField.setText(s);
+
+		} else if (box != null) {
+			box.setSelected(s.matches("(?i).*true.*"));
+		} else if (comboBox != null) {
+			int v = Integer.parseInt(s);
+			parent.suppressCombo = true;
+			comboBox.setSelectedIndex(v);
+			parent.suppressCombo = false;
+		} else if (radio != null) {
+			int v = Integer.parseInt(s);
+			radioButtonArray.get(v).setSelected(true);
+			radioButtonList[radioButtonIndex] = v;
+		}
+	}
+
+	void setDoubleValue(double v) {
+		try {
+			numberField.setText(parent.formatNum(v));
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	double getDoubleValue() {
+		double v = 0;
+		try {
+			v = LocaleHandler.getDouble(numberField.getText(),
+					LocaleHandler.localeDecimalSeparator);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return v;
+	}
+
+	void setBooleanValue(boolean v) {
+		try {
+			box.setSelected(v);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	boolean getBooleanValue() {
+		boolean v = false;
+		try {
+			v = box.isSelected();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return v;
+	}
+
+	private void handleKeyPressed(KeyEvent evt) {
+		int n = 0;
+		double sign = 0;
+		double v = sens;
+		boolean consume = true;
+		v = (evt.isShiftDown()) ? v * 0.1 : v;
+		v = (evt.isAltDown()) ? v * 0.1 : v;
+		int kcode = evt.getKeyCode();
+		if (kcode == KeyEvent.VK_ENTER) {
+			sign = 1;
+		} else if (kcode == KeyEvent.VK_HOME) {
+			n = 100;
+			sign = 1;
+		} else if (kcode == KeyEvent.VK_END) {
+			n = -100;
+			sign = 1;
+		} else if (kcode == KeyEvent.VK_PAGE_UP) {
+			n = 10;
+			sign = 1;
+		} else if (kcode == KeyEvent.VK_PAGE_DOWN) {
+			n = -10;
+			sign = 1;
+		} else if (kcode == KeyEvent.VK_DOWN) {
+			n = -1;
+			sign = 1;
+		} else if (kcode == KeyEvent.VK_UP) {
+			n = 1;
+			sign = 1;
+		} else if (kcode == KeyEvent.VK_ESCAPE) {
+			n = 0;
+			sign = -1;
+		} else {
+			consume = false;
+		}
+		if (consume) {
+			evt.consume();
+		}
+		handleIncrement(n, sign, v);
+		evt.consume();
+	}
+
+	private void handleMouseWheelMoved(MouseWheelEvent evt) {
+		double v = sens;
+		v = (evt.isShiftDown()) ? v * 0.1 : v;
+		v = (evt.isAltDown()) ? v * 0.1 : v;
+		handleIncrement(-evt.getWheelRotation(), 1, v);
+		evt.consume();
+	}
+
+	void handleIncrement(int n, double sign, double sv) {
+		if (sign != 0) {
+			if (numberField != null) {
+				String text = numberField.getText();
+				double dv = 0;
+				try {
+					dv = LocaleHandler.getDouble(text,
+							LocaleHandler.localeDecimalSeparator);
+				} catch (Exception e) {
+					System.out.println(getClass().getName() + ": Error: " + e);
+				}
+				dv += (n * sv);
+				dv *= sign;
+				dv = min(dmax, dv);
+				dv = max(dmin, dv);
+				String s = parent.formatNum(dv);
+				numberField.setText(s);
+				// parent.p("handleincrement setting value: " + s);
+			}
+			updateAllControls();
+
+		}
+	}
+
+	private void updateAllControls() {
+		parent.undoPush();
+		if (parent != null) {
+			parent.readProgramControls();
+			if (parent.selectedComponent != null) {
+				parent.selectedComponent.readObjectControls();
+			}
+			if (parent.currentTab() == Common.TAB_TABLE) {
+				parent.dataTableDisplay.updateDisplay();
+			}
+		}
+	}
+
+}
diff --git a/src/opticalraytracer/ControlPanelManager.java b/src/opticalraytracer/ControlPanelManager.java
deleted file mode 100644
index 0b9c5c8..0000000
--- a/src/opticalraytracer/ControlPanelManager.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-import javax.swing.*;
-
-/**
- *
- * @author lutusp
- */
-public class ControlPanelManager {
-
-    OpticalRayTracer parent;
-    RayTraceComputer rayTraceComputer;
-    int decPlaces = 4;
-
-    public ControlPanelManager(OpticalRayTracer p, RayTraceComputer rtc) {
-        parent = p;
-        rayTraceComputer = rtc;
-        writeLensValues(null);
-    }
-
-    public void writeLensValues(Lens p) {
-        readControls(p);
-        resetControls(p);
-    }
-
-    public void readLensValues(Lens p) {
-        writeControls(p);
-        resetControls(p);
-    }
-
-    private void writeControls(Lens p) {
-        if (p != null) {
-            writeCheckBox(parent.leftHypCheckBox, p.leftHyp);
-            writeCheckBox(parent.rightHypCheckBox, p.rightHyp);
-            writeCheckBox(parent.symmCheckBox, p.symmetrical);
-            writeTextField(parent.lensRadiusTextField, p.lensRadius);
-            writeTextField(parent.userThicknessTextField, p.userThickness);
-            writeTextField(parent.leftRadiusTextField, p.leftSphereRadius);
-            writeTextField(parent.rightRadiusTextField, p.rightSphereRadius);
-            writeTextField(parent.leftCFTextField, p.lcf);
-            writeTextField(parent.rightCFTextField, p.rcf);
-            writeTextField(parent.iorTextField, p.ior);
-            writeTextField(parent.dispersionTextField, p.dispersion);
-            writeTextField(parent.xPosTextField, p.cx);
-            writeTextField(parent.yPosTextField, p.cy);
-        }
-    }
-
-    private void readControls(Lens p) {
-        if (p != null) {
-            p.symmetrical = readCheckBox(parent.symmCheckBox);
-            p.leftHyp = readCheckBox(parent.leftHypCheckBox);
-            p.rightHyp = readCheckBox(parent.rightHypCheckBox);
-            p.lensRadius = Math.abs(readTextField(parent.lensRadiusTextField, p.lensRadius));
-            p.lensRadius = Math.abs(p.lensRadius);
-            writeTextField(parent.lensRadiusTextField, p.lensRadius);
-            p.userThickness = readTextField(parent.userThicknessTextField, p.userThickness);
-            p.userThickness = (p.userThickness < 0)?0:p.userThickness;
-            writeTextField(parent.userThicknessTextField, p.userThickness);
-            p.leftSphereRadius = readTextField(parent.leftRadiusTextField, p.leftSphereRadius);
-            p.rightSphereRadius = readTextField(parent.rightRadiusTextField, p.rightSphereRadius);
-            p.lcf = readTextField(parent.leftCFTextField, p.lcf);
-            p.rcf = readTextField(parent.rightCFTextField, p.rcf);
-            writeTextField(parent.leftCFTextField, p.lcf);
-            writeTextField(parent.rightCFTextField, p.rcf);
-            p.ior = readTextField(parent.iorTextField, p.ior);
-            p.ior = (p.ior < 1) ? 1 : p.ior;
-            writeTextField(parent.iorTextField, p.ior);
-            p.dispersion = readTextField(parent.dispersionTextField, p.dispersion);
-            p.cx = readTextField(parent.xPosTextField, p.cx);
-            p.cy = readTextField(parent.yPosTextField, p.cy);
-            p.leftSphereRadius = checkRadius(p.leftSphereRadius, p.lensRadius);
-            p.rightSphereRadius = checkRadius(p.rightSphereRadius, p.lensRadius);
-            writeTextField(parent.leftRadiusTextField, p.leftSphereRadius);
-            writeTextField(parent.rightRadiusTextField, p.rightSphereRadius);
-            p.setup();
-        }
-    }
-
-    double checkRadius(double v, double r) {
-        double sign = (v < 0) ? -1 : 1;
-        v = Math.abs(v);
-        v = Math.max(v, r);
-        return v * sign;
-    }
-
-    void resetControls(Lens p) {
-        if (p == null) {
-            resetControlsCore(false, false, false, false);
-        } else {
-            resetControlsCore(true, p.symmetrical, p.leftHyp, p.rightHyp);
-        }
-    }
-
-    void resetControlsCore(boolean global, boolean symmetrical, boolean leftHyp, boolean rightHyp) {
-        String s;
-        parent.symmCheckBox.setEnabled(global);
-        parent.lensRadiusTextField.setEnabled(global);
-        parent.userThicknessTextField.setEnabled(global);
-        parent.leftRadiusTextField.setEnabled(global);
-        parent.rightRadiusTextField.setEnabled(global && !symmetrical);
-        parent.leftCFTextField.setEnabled(global && leftHyp);
-        parent.rightCFTextField.setEnabled(global && rightHyp && !symmetrical);
-        parent.leftHypCheckBox.setEnabled(global);
-        parent.rightHypCheckBox.setEnabled(global && !symmetrical);
-        parent.iorTextField.setEnabled(global);
-        parent.dispersionTextField.setEnabled(global);
-        parent.xPosTextField.setEnabled(global);
-        parent.yPosTextField.setEnabled(global);
-        if (!global) {
-            s = "(Select a lens)";
-            parent.lensRadiusTextField.setText(s);
-            parent.userThicknessTextField.setText(s);
-            parent.leftRadiusTextField.setText(s);
-            parent.rightRadiusTextField.setText(s);
-            parent.leftCFTextField.setText(s);
-            parent.rightCFTextField.setText(s);
-            parent.dispersionTextField.setText(s);
-            parent.iorTextField.setText(s);
-            parent.xPosTextField.setText(s);
-            parent.yPosTextField.setText(s);
-        } else {
-            if (symmetrical) {
-                s = "(Symmetrical)";
-                parent.rightRadiusTextField.setText(s);
-                parent.rightCFTextField.setText(s);
-
-            } else {
-                if (!rightHyp) {
-                    parent.rightCFTextField.setText("(Spherical)");
-                }
-            }
-            if (!leftHyp) {
-                parent.leftCFTextField.setText("(Spherical)");
-            }
-        }
-
-    }
-
-    void writeCheckBox(JCheckBox cb, boolean v) {
-        cb.setSelected(v);
-    }
-
-    boolean readCheckBox(JCheckBox cb) {
-        return cb.isSelected();
-    }
-
-    void writeTextField(JTextField tf, double v) {
-        tf.setText(parent.formatNum(v));
-    }
-
-    double readTextField(JTextField tf, double v) {
-        try {
-            v = parent.getDouble(tf.getText());
-        } catch (Exception e) {
-            //System.out.println(getClass().getName() + ": Error: " + e);
-        }
-        return v;
-    }
-}
diff --git a/src/opticalraytracer/DataTableCellRenderer.java b/src/opticalraytracer/DataTableCellRenderer.java
new file mode 100644
index 0000000..853c641
--- /dev/null
+++ b/src/opticalraytracer/DataTableCellRenderer.java
@@ -0,0 +1,43 @@
+package opticalraytracer;
+
+import java.awt.Color;
+
+import javax.swing.JLabel;
+import javax.swing.table.TableCellRenderer;
+
+ at SuppressWarnings("serial")
+class DataTableCellRenderer extends JLabel implements TableCellRenderer {
+	Color evenColor;
+	Color oddColor;
+	Color[] rowColors;
+	// an array of right-justification signals
+	boolean[] rightJust;
+
+	public DataTableCellRenderer(boolean[] rightJust) {
+		this.rightJust = rightJust;
+		setOpaque(true);
+		evenColor = new Color(0xffffff);
+		oddColor = new Color(0xf0f0f0);
+		rowColors = new Color[] { evenColor, oddColor };
+	}
+
+	@Override
+	public java.awt.Component getTableCellRendererComponent(
+			javax.swing.JTable table, java.lang.Object value,
+			boolean isSelected, boolean hasFocus, int row, int column) {
+		if (value != null) {
+			setText((String) value);
+		}
+		setOpaque(true);
+		setBackground(rowColors[row % 2]);
+		setHorizontalAlignment(rightJust[column] ? JLabel.RIGHT : JLabel.LEFT);
+		setFont(table.getFont());
+		if (isSelected) {
+			setBackground(table.getSelectionBackground());
+			setForeground(table.getSelectionForeground());
+		}
+
+		return this;
+	}
+
+}
\ No newline at end of file
diff --git a/src/opticalraytracer/DataTableDisplay.java b/src/opticalraytracer/DataTableDisplay.java
new file mode 100644
index 0000000..dc7aa27
--- /dev/null
+++ b/src/opticalraytracer/DataTableDisplay.java
@@ -0,0 +1,91 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import java.awt.Font;
+import java.awt.Point;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+
+ at SuppressWarnings("serial")
+final public class DataTableDisplay extends JTable {
+	OpticalRayTracer parent;
+	DataTableModel dataTableModel;
+	int hScroll, vScroll;
+	
+	public DataTableDisplay(OpticalRayTracer p) {
+		parent = p;
+		Font origfont = getFont();
+		// change everything except the size
+		Font f = new Font(Font.MONOSPACED, Font.PLAIN,origfont.getSize());
+		setFont(f);
+		getTableHeader().setFont(f);
+		dataTableModel = new DataTableModel(p, this);
+		setModel(dataTableModel);
+		updateDisplay();
+		addFocusListener(new FocusAdapter() {
+			@Override
+			public void focusGained(FocusEvent e) {
+				updateDisplay();
+			}
+
+			@Override
+			public void focusLost(FocusEvent e) {
+				hScroll = parent.tableScrollPane.getHorizontalScrollBar()
+						.getValue();
+				vScroll = parent.tableScrollPane.getVerticalScrollBar()
+						.getValue();
+			}
+		});
+		setToolTipText("Double-click: copy line to clipboard");
+		addMouseListener(new MouseAdapter() {
+		    public void mousePressed(MouseEvent me) {
+		        JTable table =(JTable) me.getSource();
+		        Point p = me.getPoint();
+		        int row = table.rowAtPoint(p);
+		        if (me.getClickCount() == 2) {
+		            LineData ld = dataTableModel.getRowData(row);
+		            if(ld != null) {
+		            	parent.lineAnalysis.copyLineToClipboard(ld);
+		            }
+		        }
+		    }
+		});
+	}
+
+	protected void updateDisplay() {
+
+		dataTableModel.updateDisplay();
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				parent.tableScrollPane.getHorizontalScrollBar().setValue(
+						hScroll);
+				parent.tableScrollPane.getVerticalScrollBar().setValue(vScroll);
+			}
+		});
+	}
+
+}
diff --git a/src/opticalraytracer/DataTableModel.java b/src/opticalraytracer/DataTableModel.java
new file mode 100644
index 0000000..bac992a
--- /dev/null
+++ b/src/opticalraytracer/DataTableModel.java
@@ -0,0 +1,175 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.max;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.util.ArrayList;
+
+import javax.swing.JTable;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.JTableHeader;
+
+ at SuppressWarnings("serial")
+final public class DataTableModel extends AbstractTableModel {
+	OpticalRayTracer parent;
+		ArrayList<ArrayList<String>> array;
+		String[] header;
+		int[] columnWidths;
+		boolean[] rightJust;
+		JTable table;
+		FontMetrics fm;
+
+		public DataTableModel(OpticalRayTracer p, JTable table) {
+			parent = p;
+			this.table = table;
+			JTableHeader hd = table.getTableHeader(); 
+			hd.setBackground(new Color(0xc0d0c0));
+			hd.setFont(new Font("Arial",Font.BOLD,12));
+			fm = table.getFontMetrics(table.getFont());
+			array = new ArrayList<>();
+			header = parent.lineAnalysis.header;
+			columnWidths = new int[header.length];
+			rightJust = new boolean[header.length];
+		}
+		
+		protected boolean getRightJust(int i) {
+			boolean result = false;
+			if(i >= 0 && i < rightJust.length) {
+				result = rightJust[i];
+			}
+			return result;
+		}
+		
+		// this is a goofy but necessary way to
+		// pad the contents on table cells
+		protected String padString(String s) {
+			return " " + s + " ";
+		}
+
+		protected ArrayList<String> makeRow(LineData ld) {
+			ArrayList<String> row = new ArrayList<>();
+			row.add(padString(ld.fromEvent));
+			row.add(padString(ld.toEvent));
+			row.add(padString(ld.from));
+			row.add(padString(ld.to));
+			row.add(padString(ld.type));
+			for (double v : ld.numericValues()) {
+				row.add(padString(parent.formatNum(v)));
+			}
+			
+			return row;
+		}
+
+		protected void updateDisplay() {
+			if (parent.rayTraceComputer != null) {
+				parent.rayTraceComputer.traceRays(null, true);
+				if (parent.rayTraceComputer.lineList != null && parent.rayTraceComputer.lineList.size() > 0) {
+					final int limit = parent.programValues.tableLineLimit;
+					int len = parent.rayTraceComputer.lineList.size();
+					int top = (len > limit) ? limit : len;
+					String s = "Displaying " + top + " of " + len + " lines";
+					if (parent.tableDataLabel != null) {
+						parent.tableDataLabel.setText(s);
+					}
+					array.clear();
+					int marg = 8;
+					// this only works because monospace font is in use
+					int cw = (int) (fm.stringWidth("X"));
+					
+					for (int n = 0; n < columnWidths.length; n++) {
+						columnWidths[n] = header[n].length() * cw + marg;
+					}
+					int lines = 0;
+					for (LineData ld : parent.rayTraceComputer.lineList) {
+						ArrayList<String> row = makeRow(ld);
+						for (int n = 0; n < row.size(); n++) {
+							String ss = row.get(n);
+							columnWidths[n] = max(ss.length() * cw + marg,
+									columnWidths[n]);
+						}
+						array.add(row);
+						lines += 1;
+						if (lines > limit) {
+							break;
+						}
+					}
+					int ch = fm.getHeight();
+					for (int n = 0; n < getColumnCount(); n++) {
+						table.getColumnModel().getColumn(n)
+								.setMinWidth(columnWidths[n]);
+						DataTableCellRenderer rr = new DataTableCellRenderer(rightJust);
+						table.getColumnModel().getColumn(n)
+								.setCellRenderer(rr);
+					}
+					// initialize the right-justify array
+					for (int n = 0; n < getColumnCount(); n++) {
+						String ts = array.get(0).get(n);
+						// if the field contains any uppercase alphas
+						// then left-justify, otherwise right
+						rightJust[n] = !ts.matches(".*[A-Z].*");
+					}
+					table.setRowHeight(ch+8);
+				}
+			}
+		}
+		
+		protected LineData getRowData(int row) {
+			LineData result = null;
+			if(parent.rayTraceComputer.lineList != null && row >= 0 && row <= parent.rayTraceComputer.lineList.size()) {
+			result =  parent.rayTraceComputer.lineList.get(row);
+			}
+			return result;
+		}
+
+		@Override
+		public String getColumnName(int column) {
+			return header[column];
+		}
+
+		@Override
+		public int getRowCount() {
+			// TODO Auto-generated method stub
+			return array.size();
+		}
+
+		@Override
+		public int getColumnCount() {
+			// TODO Auto-generated method stub
+			return header.length;
+		}
+
+		@Override
+		public Object getValueAt(int rowIndex, int columnIndex) {
+			// TODO Auto-generated method stub
+			return array.get(rowIndex).get(columnIndex);
+		}
+
+		@Override
+		public boolean isCellEditable(int row, int col) {
+			return false;
+		}
+
+	
+}
diff --git a/src/opticalraytracer/MutableInt.java b/src/opticalraytracer/ElementBase.java
similarity index 75%
rename from src/opticalraytracer/MutableInt.java
rename to src/opticalraytracer/ElementBase.java
index 90669ff..e0bf036 100644
--- a/src/opticalraytracer/MutableInt.java
+++ b/src/opticalraytracer/ElementBase.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,23 +17,19 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
 
-/**
- *
- * @author lutusp
- */
-public class MutableInt {
+package opticalraytracer;
 
-    int v;
+import java.util.ArrayList;
 
-    public MutableInt(int v) {
-        this.v = v;
-    }
-    public MutableInt() {
-    }
+public interface ElementBase {
+	public void intersections(OpticalComponent oc, boolean leftSide,
+			Vector op1, Vector op2);
+	
+	public double lensProfileXforY(OpticalComponent oc, boolean leftSide,
+			double y, double cx);
+	
+	public double lensProfileDXforY(OpticalComponent oc, boolean leftSide,boolean entering, double y);
+	
+	public ArrayList<Vector> getPoints();
 }
diff --git a/src/opticalraytracer/ElementHyperbolic.java b/src/opticalraytracer/ElementHyperbolic.java
new file mode 100644
index 0000000..d5b05b8
--- /dev/null
+++ b/src/opticalraytracer/ElementHyperbolic.java
@@ -0,0 +1,294 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+final public class ElementHyperbolic implements ElementBase {
+	@SuppressWarnings("unused")
+	private OpticalRayTracer parent;
+	private double zValue = -1;
+	private double mValue = -1;
+	private double bValue = -1;
+	private double lrValue = -1;
+	private double invLrValue = -1;
+	private double srValue = -1;
+	private double epsilon = 1e-8;
+	// private int testCount = 0;
+	private ArrayList<Vector> points;
+	private int pointsSize = 4;
+
+	/*
+	 * This class creates hyperbolic lenses. The basic model is a conic section
+	 * of a plane through a unit cone (a cone having a slope of 1). The
+	 * curvature factor that controls the lens appearance represents a location
+	 * in the Z dimension of the plane that bisects the cone. A curvature factor
+	 * (z) of zero creates a triangular slice through the cone's apex.
+	 * Successively larger values for z produce increasingly more moderate
+	 * curvatures until as z approaches oo, the curvature approaches parabolic.
+	 * This class allows full translation and rotation of its objects, a
+	 * long-sought goal.
+	 */
+
+	public ElementHyperbolic(OpticalRayTracer p) {
+		parent = p;
+		points = new ArrayList<Vector>();
+		for (int i = 0; i < pointsSize; i++) {
+			points.add(new Vector());
+		}
+	}
+
+	public ArrayList<Vector> getPoints() {
+		return points;
+	}
+
+	/*
+	 * ------------------------------------------------------
+	 * 
+	 * begin import from
+	 * ../python_equation_generator/new_hyperbolic_equation_solutions.py
+	 * 
+	 * pa1 - pa3 = x,y intersection 1, x,y intersection 2
+	 * 
+	 * pb = profile generator for display
+	 * 
+	 * pd = first derivative of the curve profile for optical computations
+	 * 
+	 * pm = "M" factor generator
+	 * 
+	 * ------------------------------------------------------
+	 */
+
+	// From Python script:
+	// /netbackup/data/java2/OpticalRayTracer_eclipse/python_equation_generator/new_hyperbolic_equation_solutions.py
+
+	private double pa0(double z, double s, double m, double x_1, double y_1) {
+		return (-m * m * s * s * y_1 + s * x_1 - sqrt(s
+				* s
+				* (m * m * s * s * x_1 * x_1 - m * m * s * s * z - 2 * m * m
+						* s * x_1 * y_1 + m * m * y_1 * y_1 + z)))
+				/ (s * (m * m * s * s - 1));
+	}
+
+	private double pa1(double z, double s, double m, double x_1, double y_1) {
+		return (-m * m * s * s * y_1 + s * x_1 - sqrt(s
+				* s
+				* (m * m * s * s * x_1 * x_1 - m * m * s * s * z - 2 * m * m
+						* s * x_1 * y_1 + m * m * y_1 * y_1 + z)))
+				/ (m * m * s * s - 1);
+	}
+
+	private double pa2(double z, double s, double m, double x_1, double y_1) {
+		return (-m * m * s * s * y_1 + s * x_1 + sqrt(s
+				* s
+				* (m * m * s * s * x_1 * x_1 - m * m * s * s * z - 2 * m * m
+						* s * x_1 * y_1 + m * m * y_1 * y_1 + z)))
+				/ (s * (m * m * s * s - 1));
+	}
+
+	private double pa3(double z, double s, double m, double x_1, double y_1) {
+		return (-m * m * s * s * y_1 + s * x_1 + sqrt(s
+				* s
+				* (m * m * s * s * x_1 * x_1 - m * m * s * s * z - 2 * m * m
+						* s * x_1 * y_1 + m * m * y_1 * y_1 + z)))
+				/ (m * m * s * s - 1);
+	}
+
+	private double pb(double y, double z, double m, double x_1, double y_1) {
+		return -x_1
+				- sqrt(m * m * y * y + 2 * m * m * y * y_1 + m * m * y_1 * y_1
+						+ z);
+	}
+
+	private double pd(double y, double z, double m, double y_1) {
+		return -m
+				* m
+				* (y + y_1)
+				/ sqrt(m * m * y * y + 2 * m * m * y * y_1 + m * m * y_1 * y_1
+						+ z);
+	}
+
+	private double pm(double z, double r) {
+		return -sqrt(r * r + 2 * sqrt(r * r * z));
+	}
+
+	/*
+	 * ------------------------------------------------------
+	 * 
+	 * end import from
+	 * ../python_equation_generator/new_hyperbolic_equation_solutions.py
+	 * 
+	 * ------------------------------------------------------
+	 */
+
+	public void intersections(OpticalComponent oc, boolean leftSide,
+			Vector op1, Vector op2) {
+
+		for (Vector p : points) {
+			p.assign(Vector.invalidState());
+		}
+
+		double signedScale = oc.signedScale(leftSide);
+		double lensRadius = oc.lensRadius();
+		double angleRadians = oc.angleRadians();
+
+		Vector thr, tc, p1, p2;
+		double s;
+
+		// a somewhat hacky way to avoid the on-axis zero problem
+		while (true) {
+			thr = new Vector(-oc.signedThickness(leftSide), 0)
+					.rotate(angleRadians);
+			tc = new Vector(oc.xPos(), oc.yPos()).translate(thr);
+			p1 = new Vector(op1).translateSub(tc).rotate(-angleRadians)
+					.scale(1 / signedScale, 1 / lensRadius);
+			p2 = new Vector(op2).translateSub(tc).rotate(-angleRadians)
+					.scale(1 / signedScale, 1 / lensRadius);
+
+			// the all-important line slope value
+			// which must not approach zero
+			s = (p2.y - p1.y) / (p2.x - p1.x);
+
+			if (abs(s) >= epsilon) {
+				break;
+			}
+			angleRadians += epsilon;
+		}
+
+		updateFactors(oc, leftSide);
+
+		Vector pt;
+		int len_d2 = points.size() / 2;
+		int n = 0;
+		Iterator<Vector> ic = points.iterator();
+		// must compute four possible intersections
+		// each with either sign of the bias variable
+		// this is the only way to allow multiple reflections
+		// on the surface of a reflector
+		double sb = (s < 0) ? -bValue : bValue;
+		double sbb;
+
+		while (ic.hasNext()) {
+			sbb = (n >= len_d2) ? -sb : sb;
+			pt = ic.next();
+			pt.x = pa0(zValue, s, mValue, p1.x - sbb, p1.y) + p1.x;
+			pt.y = pa1(zValue, s, mValue, p1.x - sbb, p1.y) + p1.y;
+			pt = ic.next();
+			pt.x = pa2(zValue, s, mValue, p1.x + sbb, p1.y) + p1.x;
+			pt.y = pa3(zValue, s, mValue, p1.x + sbb, p1.y) + p1.y;
+			n += 2;
+		}
+
+		double count = 0;
+		double minx = 1e6, maxx = -1e6;
+		// filter out spurious surface detections
+		for (Vector p : points) {
+			if (leftSide) {
+				p.x = (p.x < 0) ? Double.NaN : p.x;
+			} else {
+				p.x = (p.x > 0) ? Double.NaN : p.x;
+			}
+			if (!Double.isNaN(p.x)) {
+				minx = min(minx, p.x);
+				maxx = max(maxx, p.x);
+				count += 1;
+			}
+		}
+
+		if (!oc.isReflector() && count > 1) {
+
+			for (Vector p : points) {
+				p.x = (p.x < 0 && p.x == minx) ? Double.NaN : p.x;
+				p.x = (p.x > 0 && p.x == maxx) ? Double.NaN : p.x;
+			}
+		}
+
+		// parent.p("intersections:");
+		for (Vector p : points) {
+			// parent.p("" + p);
+			p.assign(p.scale(signedScale, lensRadius).rotate(angleRadians)
+					.translate(tc));
+		}
+
+	}
+
+	// hyperbolic curve profile
+	public double lensProfileXforY(OpticalComponent oc, boolean leftSide,
+			double y, double cx) {
+		updateFactors(oc, leftSide);
+		return lensProfileXforYCore(oc, leftSide, y, cx);
+	}
+
+	// hyperbolic curve profile
+	public double lensProfileXforYCore(OpticalComponent oc, boolean leftSide,
+			double y, double cx) {
+		double x = -pb(y / oc.lensRadius(), zValue, mValue, cx + bValue, 0)
+				* oc.scale(leftSide) * oc.radiusSign(leftSide) - oc.thickness();
+		if (leftSide) {
+			x = -x;
+		}
+		// testCount += 1;
+		// if(testCount % 101 == 0) {
+		// parent.p("hyperbolic profile: left: " + leftSide + ", y = " + y +
+		// ",v = " + x);
+		// }
+		return x;
+	}
+
+	// 1st derivative of curve profile
+	public double lensProfileDXforY(OpticalComponent oc, boolean leftSide,
+			boolean entering, double y) {
+
+		updateFactors(oc, leftSide);
+		// note the square factor divisor
+		double dx = pd(y * invLrValue, zValue, mValue, 0) * oc.scale(leftSide)
+				* oc.radiusSign(leftSide) * invLrValue;
+		if (leftSide) {
+			dx = -dx;
+		}
+		// parent.p("hyperbolic DX: left: " + leftSide + ", needRotation: "
+		// + needRotation + ", y: " + y + ", sr: " + srValue + ", dx: "
+		// + v);
+		return -dx;
+	}
+
+	private void updateFactors(OpticalComponent oc, boolean leftSide) {
+		double newZ = oc.zValue(leftSide);
+		double newSR = oc.sphereRadius(leftSide);
+		double newLR = oc.lensRadius();
+		if (zValue != newZ || newLR != lrValue || newSR != srValue) {
+			mValue = pm(newZ, 1);
+			bValue = pb(1, newZ, mValue, 0, 0);
+			// testCount += 1;
+			// if(testCount % 10 == 0) {
+			// parent.p("hyperbolic updateFactors recalc: z: " + newZ
+			// + ", m = " + mValue + ", b = " + bValue);
+			// }
+			zValue = newZ;
+			lrValue = newLR;
+			invLrValue = 1 / newLR;
+			srValue = newSR;
+		}
+	}
+}
diff --git a/src/opticalraytracer/ElementParabolic.java b/src/opticalraytracer/ElementParabolic.java
new file mode 100644
index 0000000..d34101f
--- /dev/null
+++ b/src/opticalraytracer/ElementParabolic.java
@@ -0,0 +1,306 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.abs;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.lang.Math.sqrt;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+final public class ElementParabolic implements ElementBase {
+	OpticalRayTracer parent;
+	private double srValue = -1;
+	private double lrValue = -1;
+	private double mValue = -1;
+	private double bValue = -1;
+	// private double epsilon = 1e-3;
+	double leftMax, rightMax;
+	private ArrayList<Vector> points;
+	private int pointsSize = 8;
+	private double epsilon = 1e-6;
+
+	// private int testCount = 0;
+
+	public ElementParabolic(OpticalRayTracer p) {
+		parent = p;
+		points = new ArrayList<Vector>();
+		for (int i = 0; i < pointsSize; i++) {
+			points.add(new Vector());
+		}
+	}
+
+	public ArrayList<Vector> getPoints() {
+		return points;
+	}
+
+	/*
+	 * ------------------------------------------------------
+	 * 
+	 * begin import from
+	 * ../python_equation_generator/new_parabolic_equation_solutions.py
+	 * 
+	 * pa1 - pa3 = x,y intersection 1, x,y intersection 2
+	 * 
+	 * pb0, pb1 = profile generators for display
+	 * 
+	 * pc0 = first derivative of the curve profile for optical computations
+	 * 
+	 * ------------------------------------------------------
+	 */
+
+	// From Python script:
+	// /netbackup/data/java2/OpticalRayTracer_eclipse/python_equation_generator/new_parabolic_equation_solutions.py
+
+	private double pa0(double s, double x_1, double y_1) {
+		return (-2 * s * y_1 - sqrt(4 * (s * s) * x_1 - 4 * s * y_1 + 1) + 1)
+				/ (2 * (s * s));
+	}
+
+	private double pa1(double s, double x_1, double y_1) {
+		return (-2 * s * y_1 - sqrt(4 * (s * s) * x_1 - 4 * s * y_1 + 1) + 1)
+				/ (2 * s);
+	}
+
+	private double pa2(double s, double x_1, double y_1) {
+		return (-2 * s * y_1 + sqrt(4 * (s * s) * x_1 - 4 * s * y_1 + 1) + 1)
+				/ (2 * (s * s));
+	}
+
+	private double pa3(double s, double x_1, double y_1) {
+		return (-2 * s * y_1 + sqrt(4 * (s * s) * x_1 - 4 * s * y_1 + 1) + 1)
+				/ (2 * s);
+	}
+
+	private double pa4(double s, double x_1, double y_1) {
+		return -(2 * s * y_1 + sqrt(-4 * (s * s) * x_1 + 4 * s * y_1 + 1) + 1)
+				/ (2 * (s * s));
+	}
+
+	private double pa5(double s, double x_1, double y_1) {
+		return -(2 * s * y_1 + sqrt(-4 * (s * s) * x_1 + 4 * s * y_1 + 1) + 1)
+				/ (2 * s);
+	}
+
+	private double pa6(double s, double x_1, double y_1) {
+		return (-2 * s * y_1 + sqrt(-4 * (s * s) * x_1 + 4 * s * y_1 + 1) - 1)
+				/ (2 * (s * s));
+	}
+
+	private double pa7(double s, double x_1, double y_1) {
+		return (-2 * s * y_1 + sqrt(-4 * (s * s) * x_1 + 4 * s * y_1 + 1) - 1)
+				/ (2 * s);
+	}
+
+	private double pb(double y, double x_1, double y_1) {
+		return -x_1 + ((y + y_1) * (y + y_1));
+	}
+
+	private double pd(double y, double y_1) {
+		return 2 * y + 2 * y_1;
+	}
+
+	/*
+	 * ------------------------------------------------------
+	 * 
+	 * end import from
+	 * ../python_equation_generator/new_circular_equation_solutions.py
+	 * 
+	 * ------------------------------------------------------
+	 */
+
+	public void intersections(OpticalComponent oc, boolean leftSide,
+			Vector op1, Vector op2) {
+
+		for (Vector p : points) {
+			p.assign(Vector.invalidState());
+		}
+
+		double signedScale = oc.signedScale(leftSide);
+		double lensRadius = oc.lensRadius();
+		double angleRadians = oc.angleRadians();
+
+		Vector thr, tc, p1, p2;
+		double s;
+
+		// a somewhat hacky way to avoid the on-axis zero problem
+		while (true) {
+			thr = new Vector(-oc.signedThickness(leftSide), 0)
+					.rotate(angleRadians);
+			tc = new Vector(oc.xPos(), oc.yPos()).translate(thr);
+			p1 = new Vector(op1).translateSub(tc).rotate(-angleRadians)
+					.scale(1 / signedScale, 1 / lensRadius);
+			p2 = new Vector(op2).translateSub(tc).rotate(-angleRadians)
+					.scale(1 / signedScale, 1 / lensRadius);
+
+			// the all-important line slope value
+			// which must not approach zero
+			s = (p2.y - p1.y) / (p2.x - p1.x);
+
+			if (abs(s) >= epsilon) {
+				break;
+			}
+			angleRadians += epsilon;
+		}
+
+		updateFactors(oc, leftSide);
+
+		Vector pt;
+		int n = 0;
+		int len_d2 = points.size() / 2;
+		Iterator<Vector> ic = points.iterator();
+		// must compute four possible intersections
+		// each with either sign of the bias variable
+		// this is the only way to allow multiple reflections
+		// on the surface of a reflector
+		// parent.pr("values: " + p1 + "," + p2 + ", s value:" + s);
+		// double radiusSign = oc.radiusSign(leftSide);
+		double sb = -1;
+		double sbb;
+
+		while (ic.hasNext()) {
+			sbb = (n < len_d2) ? -sb : sb;
+			pt = ic.next();
+			pt.x = pa0(s, p1.x - sbb, p1.y) + p1.x;
+			pt.y = pa1(s, p1.x - sbb, p1.y) + p1.y;
+			pt = ic.next();
+			pt.x = pa2(s, p1.x + sbb, p1.y) + p1.x;
+			pt.y = pa3(s, p1.x + sbb, p1.y) + p1.y;
+			pt = ic.next();
+			pt.x = pa4(s, p1.x - sbb, p1.y) + p1.x;
+			pt.y = pa5(s, p1.x - sbb, p1.y) + p1.y;
+			pt = ic.next();
+			pt.x = pa6(s, p1.x + sbb, p1.y) + p1.x;
+			pt.y = pa7(s, p1.x + sbb, p1.y) + p1.y;
+			n += 4;
+		}
+
+		double slmaxx = leftMax * 2 / abs(signedScale);
+		double srmaxx = rightMax * 2 / abs(signedScale);
+
+		double count = 0;
+		double minx = 1e6, maxx = -1e6;
+		// filter out spurious surface detections
+		for (Vector p : points) {
+
+			if (leftSide) {
+				p.x = (p.x < 0) ? Double.NaN : p.x;
+				p.x = (p.x > slmaxx) ? Double.NaN : p.x;
+			} else {
+				p.x = (p.x > 0) ? Double.NaN : p.x;
+				p.x = (p.x < -srmaxx) ? Double.NaN : p.x;
+			}
+			if (!Double.isNaN(p.x)) {
+				minx = min(minx, p.x);
+				maxx = max(maxx, p.x);
+				count += 1;
+			}
+		}
+
+		if (!oc.isReflector() && count > 1) {
+
+			// if count > 1, the maxima
+			// or minima are invalid
+			for (Vector p : points) {
+				if (p.x < 0) {
+					p.x = (p.x == minx) ? Double.NaN : p.x;
+				} else {
+					p.x = (p.x == maxx) ? Double.NaN : p.x;
+				}
+			}
+		}
+
+		// parent.p("intersections for left: " + leftSide);
+		for (Vector p : points) {
+			p.assign(p.scale(signedScale, lensRadius).rotate(angleRadians)
+					.translate(tc));
+			// p.scale(signedScale, lensRadius);
+			// p.rotate(oc.angleRadians);
+			// p.translate(tc);
+			// parent.p("" + p);
+		}
+	}
+
+	public double lensProfileXforY(OpticalComponent oc, boolean leftSide,
+			double y, double cx) {
+		updateFactors(oc, leftSide);
+		return lensProfileXforYCore(oc, leftSide, y, cx);
+
+	}
+
+	private double lensProfileXforYCore(OpticalComponent oc, boolean leftSide,
+			double y, double cx) {
+		double x = pb(y, cx + bValue, 0) * mValue * oc.scale(leftSide)
+				* oc.radiusSign(leftSide) - oc.thickness();
+		if (leftSide) {
+			x = -x;
+		}
+		// testCount += 1;
+		// if (testCount % 101 == 0) {
+		// parent.p("parabolic profile: left: " + leftSide + ", y: " + y
+		// + ", x: " + x + ", b: " + bValue);
+		// }
+		return x;
+	}
+
+	// 1st derivative
+	public double lensProfileDXforY(OpticalComponent oc, boolean leftSide,
+			boolean entering, double y) {
+		// parent.p("parabolic DX left: " + leftSide);
+		updateFactors(oc, leftSide);
+		double dx = pd(y, 0) * mValue * oc.scale(leftSide)
+				* oc.radiusSign(leftSide);
+		if (leftSide) {
+			dx = -dx;
+		}
+		// parent.p("parabolic DX: left: " + leftSide + ", needRotation: " +
+		// needRotation + ", y: " + y + ", sr: " +
+		// srValue + ", dx: " + v);
+		return dx;
+	}
+
+	private void updateFactors(OpticalComponent oc, boolean leftSide) {
+		double newSR = oc.sphereRadius(leftSide);
+		double newLR = oc.lensRadius();
+		// double newSRA = abs(newSR);
+		if (srValue != newSR || lrValue != newLR) {
+			mValue = 1 / (newLR * newLR);
+			bValue = pb(newLR, 0, 0);
+
+			srValue = newSR;
+			lrValue = newLR;
+			double a = abs(lensProfileXforYCore(oc, true, 0, 0));
+			double b = abs(lensProfileXforYCore(oc, true, newLR, 0));
+			leftMax = max(a, b);
+			a = abs(lensProfileXforYCore(oc, false, 0, 0));
+			b = abs(lensProfileXforYCore(oc, false, newLR, 0));
+			rightMax = max(a, b);
+			// parent.pr("parabolic updateFactors recalc: sr: " + newSR +
+			// ", lr: "
+			// + newLR + ", m: " + mValue + ", b = " + bValue + ", maxx: "
+			// + maxx + ", minx: " + minx);
+
+		}
+	}
+
+}
diff --git a/src/opticalraytracer/ElementPlanar.java b/src/opticalraytracer/ElementPlanar.java
new file mode 100644
index 0000000..e8f3893
--- /dev/null
+++ b/src/opticalraytracer/ElementPlanar.java
@@ -0,0 +1,56 @@
+package opticalraytracer;
+
+import java.util.ArrayList;
+
+final public class ElementPlanar implements ElementBase {
+	OpticalRayTracer parent;
+	private ArrayList<Vector> points;
+	Vector pt;
+
+	// private double epsilon = 1e-8;
+
+	public ElementPlanar(OpticalRayTracer p) {
+		parent = p;
+		points = new ArrayList<Vector>();
+		pt = new Vector();
+		points.add(pt);
+	}
+
+	@Override
+	public void intersections(OpticalComponent oc, boolean leftSide,
+			Vector op1, Vector op2) {
+
+		double angleRadians = oc.angleRadians();
+
+		Vector thr = new Vector(-oc.signedThickness(leftSide), 0)
+				.rotate(angleRadians);
+		Vector tc = new Vector(oc.xPos(), oc.yPos()).translate(thr);
+		Vector p1 = new Vector(op1).translateSub(tc).rotate(-angleRadians);
+		Vector p2 = new Vector(op2).translateSub(tc).rotate(-angleRadians);
+
+		pt.x = 0;
+		pt.y = Common.ntrp(pt.x, p1.x, p2.x, p1.y, p2.y);
+		pt.assign(pt.rotate(angleRadians).translate(tc));
+	}
+
+	@Override
+	public double lensProfileXforY(OpticalComponent oc, boolean leftSide,
+			double y, double cx) {
+		double x = (leftSide) ? oc.thickness() : -oc.thickness();
+		// this unattractive hack avoids an overflow when x is perfectly vertical
+		return x + y * 1e-9;
+	}
+
+	@Override
+	public double lensProfileDXforY(OpticalComponent oc, boolean leftSide,
+			boolean entering, double y) {
+		// always vertical, therefore x' = 0
+		return 0;
+	}
+
+	@Override
+	public ArrayList<Vector> getPoints() {
+		return points;
+	}
+
+}
diff --git a/src/opticalraytracer/ElementSpherical.java b/src/opticalraytracer/ElementSpherical.java
new file mode 100644
index 0000000..894a3af
--- /dev/null
+++ b/src/opticalraytracer/ElementSpherical.java
@@ -0,0 +1,232 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+final public class ElementSpherical implements ElementBase {
+	OpticalRayTracer parent;
+	private double srValue = -1;
+	private double lrValue = -1;
+	private double bValue = -1;
+	private double epsilon = 1e-8;
+	// private int testCount = 0;
+	private ArrayList<Vector> points;
+	private int pointsSize = 4;
+
+	public ElementSpherical(OpticalRayTracer p) {
+		parent = p;
+		points = new ArrayList<Vector>();
+		for (int i = 0; i < pointsSize; i++) {
+			points.add(new Vector());
+		}
+	}
+
+	public ArrayList<Vector> getPoints() {
+		return points;
+	}
+
+	/*
+	 * ------------------------------------------------------
+	 * 
+	 * begin import from
+	 * ../python_equation_generator/new_circular_equation_solutions.py
+	 * 
+	 * pa1 - pa3 = x,y intersection 1, x,y intersection 2
+	 * 
+	 * pb0, pb1 = profile generators for display
+	 * 
+	 * pc0 = first derivative of the curve profile for optical computations
+	 * 
+	 * ------------------------------------------------------
+	 */
+
+	// From Python script:
+	// /netbackup/data/java2/OpticalRayTracer_eclipse/python_equation_generator/new_spherical_equation_solutions.py
+
+	private double pa0(double r, double s, double x_1, double y_1) {
+		return -(s * s * y_1 + s * x_1 + sqrt(s
+				* s
+				* (r * r * s * s + r * r - s * s * x_1 * x_1 + 2 * s * x_1
+						* y_1 - y_1 * y_1)))
+				/ (s * (s * s + 1));
+	}
+
+	private double pa1(double r, double s, double x_1, double y_1) {
+		return -(s * s * y_1 + s * x_1 + sqrt(s
+				* s
+				* (r * r * s * s + r * r - s * s * x_1 * x_1 + 2 * s * x_1
+						* y_1 - y_1 * y_1)))
+				/ (s * s + 1);
+	}
+
+	private double pa2(double r, double s, double x_1, double y_1) {
+		return (-s * (s * y_1 + x_1) + sqrt(s
+				* s
+				* (r * r * s * s + r * r - s * s * x_1 * x_1 + 2 * s * x_1
+						* y_1 - y_1 * y_1)))
+				/ (s * (s * s + 1));
+	}
+
+	private double pa3(double r, double s, double x_1, double y_1) {
+		return (-s * (s * y_1 + x_1) + sqrt(s
+				* s
+				* (r * r * s * s + r * r - s * s * x_1 * x_1 + 2 * s * x_1
+						* y_1 - y_1 * y_1)))
+				/ (s * s + 1);
+	}
+
+	private double pb(double y, double r, double x_1, double y_1) {
+		return -x_1 - sqrt(-(-r + y + y_1) * (r + y + y_1));
+	}
+
+	private double pd(double y, double r, double y_1) {
+		return -sqrt(-(-r + y + y_1) * (r + y + y_1)) * (y + y_1)
+				/ ((-r + y + y_1) * (r + y + y_1));
+	}
+
+	/*
+	 * ------------------------------------------------------
+	 * 
+	 * end import from
+	 * ../python_equation_generator/new_circular_equation_solutions.py
+	 * 
+	 * ------------------------------------------------------
+	 */
+
+	public void intersections(OpticalComponent oc, boolean leftSide,
+			Vector op1, Vector op2) {
+		// Common.p("---------- intersections ------------");
+
+		for (Vector p : points) {
+			p.assign(Vector.invalidState());
+		}
+
+		double angleRadians = oc.angleRadians();
+
+		Vector thr, tc, p1, p2;
+		double s;
+
+		// a somewhat hacky way to avoid the on-axis zero problem
+		while (true) {
+			thr = new Vector(-oc.signedThickness(leftSide), 0)
+					.rotate(angleRadians);
+			tc = new Vector(oc.xPos(), oc.yPos()).translate(thr);
+			p1 = new Vector(op1).translateSub(tc).rotate(-angleRadians);
+			p2 = new Vector(op2).translateSub(tc).rotate(-angleRadians);
+
+			// the all-important line slope value
+			// which must not approach zero
+			s = (p2.y - p1.y) / (p2.x - p1.x);
+
+			if (abs(s) >= epsilon) {
+				break;
+			}
+			angleRadians += epsilon;
+		}
+		
+		updateFactors(oc, leftSide);
+
+		Vector pt;
+		int len_d2 = points.size() / 2;
+		int n = 0;
+		Iterator<Vector> ic = points.iterator();
+		// must compute four possible intersections
+		// each with either sign of the bias variable
+		// this is the only way to allow multiple internal reflections
+		// on the surface of a reflector
+		double sb = bValue;
+		double sbb;
+		while (ic.hasNext()) {
+			sbb = (n >= len_d2) ? -sb : sb;
+			pt = ic.next();
+			pt.x = pa0(srValue, s, p1.x - sbb, p1.y) + p1.x;
+			pt.y = pa1(srValue, s, p1.x - sbb, p1.y) + p1.y;
+			pt = ic.next();
+			pt.x = pa2(srValue, s, p1.x + sbb, p1.y) + p1.x;
+			pt.y = pa3(srValue, s, p1.x + sbb, p1.y) + p1.y;
+			n += 2;
+		}
+
+		// test, then restore original rotation and position
+		for (Vector p : points) {
+			if (leftSide ^ oc.radiusSign(leftSide) < 0) {
+				p.x = (p.x < 0) ? Double.NaN : p.x;
+			} else {
+				p.x = (p.x > 0) ? Double.NaN : p.x;
+			}
+			p.assign(p.rotate(angleRadians).translate(tc));
+		}
+	}
+
+	public double lensProfileXforY(OpticalComponent oc, boolean leftSide,
+			double y, double cx) {
+		updateFactors(oc, leftSide);
+		return lensProfileXforYCore(oc, leftSide, y, cx);
+	}
+
+	public double lensProfileXforYCore(OpticalComponent oc, boolean leftSide,
+			double y, double cx) {
+		double x = pb(y, srValue, cx + bValue, 0) * oc.radiusSign(leftSide)
+				- oc.thickness();
+		if (leftSide) {
+			x = -x;
+		}
+		// testCount += 1;
+		// if (testCount % 101 == 0) {
+		// parent.p("spherical profile: left: " + leftSide + ", y: " + y
+		// + ", x: " + x + ", b: " + bValue);
+		// }
+		return x;
+	}
+
+	// 1st derivative
+	public double lensProfileDXforY(OpticalComponent oc, boolean leftSide,
+			boolean entering, double y) {
+		updateFactors(oc, leftSide);
+		double dx = pd(y, srValue, 0) * oc.radiusSign(leftSide);
+		if (leftSide) {
+			dx = -dx;
+		}
+		// Common.p("spherical DX: left: " + leftSide + ", needRotation: " +
+		// needRotation + ", y: " + y + ", sr: " +
+		// srValue + ", dx: " + dx);
+		return dx;
+	}
+
+	private void updateFactors(OpticalComponent oc, boolean leftSide) {
+		double newSR = oc.sphereRadius(leftSide);
+		double newLR = oc.lensRadius();
+		double newSRA = abs(newSR);
+		if (srValue != newSR || lrValue != newLR) {
+			// mValue = pm(newSR);
+			bValue = pb(newLR, newSRA, 0, 0);
+			// parent.p("spherical updateFactors recalc: r: " + newSR + ", lr: "
+			// + newLR + ", b = " + bValue);
+			srValue = newSR;
+			lrValue = newLR;
+		}
+	}
+
+}
diff --git a/src/opticalraytracer/GraphicDisplay.form b/src/opticalraytracer/GraphicDisplay.form
deleted file mode 100644
index 23031e7..0000000
--- a/src/opticalraytracer/GraphicDisplay.form
+++ /dev/null
@@ -1,126 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" ?>
-
-<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
-  <NonVisualComponents>
-    <Container class="javax.swing.JPopupMenu" name="popupMenu">
-
-      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
-        <Property name="useNullLayout" type="boolean" value="true"/>
-      </Layout>
-      <SubComponents>
-        <MenuItem class="javax.swing.JMenuItem" name="newLensMenuItem">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/document-new.png"/>
-            </Property>
-            <Property name="text" type="java.lang.String" value="New Lens"/>
-            <Property name="toolTipText" type="java.lang.String" value="Create new lens at cursor position"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="newLensMenuItemMouseReleased"/>
-          </Events>
-        </MenuItem>
-        <MenuItem class="javax.swing.JMenuItem" name="cutMenuItem">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/edit-cut.png"/>
-            </Property>
-            <Property name="text" type="java.lang.String" value="Cut"/>
-            <Property name="toolTipText" type="java.lang.String" value="Cut Selected Lens"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="cutMenuItemMouseReleased"/>
-          </Events>
-        </MenuItem>
-        <MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/edit-copy.png"/>
-            </Property>
-            <Property name="text" type="java.lang.String" value="Copy"/>
-            <Property name="toolTipText" type="java.lang.String" value="Copy Selected Lens"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="copyMenuItemMouseReleased"/>
-          </Events>
-        </MenuItem>
-        <MenuItem class="javax.swing.JMenuItem" name="pasteCursorMenuItem">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/edit-paste.png"/>
-            </Property>
-            <Property name="text" type="java.lang.String" value="Paste: mouse cursor"/>
-            <Property name="toolTipText" type="java.lang.String" value="Paste lens to mouse cursor position"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="pasteCursorMenuItemMouseReleased"/>
-          </Events>
-        </MenuItem>
-        <MenuItem class="javax.swing.JMenuItem" name="pasteAbsMenuItem">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/edit-paste.png"/>
-            </Property>
-            <Property name="text" type="java.lang.String" value="Paste: defined position"/>
-            <Property name="toolTipText" type="java.lang.String" value="Paste lens to its defined position"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="pasteAbsMenuItemMouseReleased"/>
-          </Events>
-        </MenuItem>
-        <MenuItem class="javax.swing.JMenuItem" name="deleteMenuItem">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/process-stop.png"/>
-            </Property>
-            <Property name="text" type="java.lang.String" value="Delete"/>
-            <Property name="toolTipText" type="java.lang.String" value="Delete Selected Lens"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="deleteMenuItemMouseReleased"/>
-          </Events>
-        </MenuItem>
-      </SubComponents>
-    </Container>
-  </NonVisualComponents>
-  <Properties>
-    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-      <Color blue="ff" green="ff" id="white" palette="1" red="ff" type="palette"/>
-    </Property>
-    <Property name="toolTipText" type="java.lang.String" value="<html>&#xa;Zoom = mouse wheel<br/>&#xa;Pan = drag mouse<br/>&#xa;</html>"/>
-  </Properties>
-  <Events>
-    <EventHandler event="mouseWheelMoved" listener="java.awt.event.MouseWheelListener" parameters="java.awt.event.MouseWheelEvent" handler="formMouseWheelMoved"/>
-    <EventHandler event="mouseEntered" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="formMouseEntered"/>
-    <EventHandler event="mouseExited" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="formMouseExited"/>
-    <EventHandler event="mousePressed" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="formMousePressed"/>
-    <EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="formMouseReleased"/>
-    <EventHandler event="componentResized" listener="java.awt.event.ComponentListener" parameters="java.awt.event.ComponentEvent" handler="formComponentResized"/>
-    <EventHandler event="mouseDragged" listener="java.awt.event.MouseMotionListener" parameters="java.awt.event.MouseEvent" handler="formMouseDragged"/>
-    <EventHandler event="mouseMoved" listener="java.awt.event.MouseMotionListener" parameters="java.awt.event.MouseEvent" handler="formMouseMoved"/>
-  </Events>
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-  </AuxValues>
-
-  <Layout>
-    <DimensionLayout dim="0">
-      <Group type="103" groupAlignment="0" attributes="0">
-          <EmptySpace min="0" pref="400" max="32767" attributes="0"/>
-      </Group>
-    </DimensionLayout>
-    <DimensionLayout dim="1">
-      <Group type="103" groupAlignment="0" attributes="0">
-          <EmptySpace min="0" pref="300" max="32767" attributes="0"/>
-      </Group>
-    </DimensionLayout>
-  </Layout>
-</Form>
diff --git a/src/opticalraytracer/GraphicDisplay.java b/src/opticalraytracer/GraphicDisplay.java
index 9aa3ca2..9ff2d98 100644
--- a/src/opticalraytracer/GraphicDisplay.java
+++ b/src/opticalraytracer/GraphicDisplay.java
@@ -1,488 +1,750 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-/*
- * graphicDsiplay.java
- *
- * Created on Mar 17, 2009, 8:20:18 AM
- */
 package opticalraytracer;
 
-import java.awt.*;
-
-
-import java.awt.image.*;
-import java.awt.Image.*;
-import java.awt.event.*;
-import java.util.*;
-
-/**
- *
- * @author lutusp
- */
-final public class GraphicDisplay extends javax.swing.JPanel {
-
-    boolean mouseInside = false;
-    OpticalRayTracer parent;
-    RayTraceComputer rayTraceComputer;
-    MutableDouble dx1 = new MutableDouble();
-    MutableDouble dx2 = new MutableDouble();
-    MutableDouble dy1 = new MutableDouble();
-    MutableDouble dy2 = new MutableDouble();
-    MutableDouble dx = new MutableDouble();
-    MutableDouble dy = new MutableDouble();
-    MutableInt ix = new MutableInt();
-    MutableInt iy = new MutableInt();
-    MutableDouble xa = new MutableDouble();
-    MutableDouble ya = new MutableDouble();
-    MutableDouble xb = new MutableDouble();
-    MutableDouble yb = new MutableDouble();
-
-    Cursor handCursor,moveCursor,defaultCursor;
-
-    /** Creates new form graphicDsiplay */
-    public GraphicDisplay(OpticalRayTracer p, RayTraceComputer rtc) {
-        parent = p;
-        rayTraceComputer = rtc;
-        handCursor = new Cursor(Cursor.HAND_CURSOR);
-        moveCursor = new Cursor(Cursor.MOVE_CURSOR);
-        defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
-        initComponents();
-    }
-
-    @Override
-    public void paintComponent(Graphics g) {
-        int x = getWidth();
-        int y = getHeight();
-        rayTraceProcessCore(x, y);
-        g.drawImage(parent.image, 0, 0, null);
-    }
-
-    // allows drawing any size
-    void drawData(Graphics g, int x, int y) {
-        rayTraceProcessCore(x, y);
-        g.drawImage(parent.image, 0, 0, null);
-    }
-
-    void rayTraceProcess(boolean paint) {
-        int x = getWidth();
-        int y = getHeight();
-        rayTraceProcessCore(x, y);
-        if (paint) {
-            repaint();
-        }
-    }
-
-    void rayTraceProcessCore(int x, int y) {
-        if (updateBuffer(x, y, false)) {
-            parent.unselectButton.setEnabled(parent.selectedLens != null);
-            Graphics2D bg = (Graphics2D) parent.image.getGraphics();
-            bg.setColor(parent.inverted ? parent.sv_dispLoColor.getColor() : parent.sv_dispHiColor.getColor());
-            bg.fillRect(0, 0, parent.xSize, parent.ySize);
-            if (parent.beamWidth > 1) {
-                bg.setStroke(new BasicStroke(parent.beamWidth));
-            }
-            if (parent.drawGrid) {
-                rayTraceComputer.drawGrid(bg);
-                rayTraceComputer.drawBaselines(bg);
-            }
-            if (parent.antiAlias) {
-                RenderingHints rh = new RenderingHints(
-                        RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-                bg.addRenderingHints(rh);
-            }
-            rayTraceComputer.drawLenses(bg);
-            rayTraceComputer.drawBoxes(bg);
-            rayTraceComputer.traceRays(bg, x, y);
-            bg.dispose();
-        }
-    }
-
-    boolean updateBuffer(int x, int y, boolean rayTrace) {
-        boolean success = false;
-        if (x > 0 && y > 0) {
-            success = true;
-            if (parent.image == null || parent.xSize != x || parent.ySize != y) {
-                parent.xSize = x;
-                parent.ySize = y;
-                parent.image = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB);
-                parent.xCenter = parent.xSize / 2;
-                parent.yCenter = parent.ySize / 2;
-            }
-        }
-        return success;
-    }
-
-    void updateStatusBar(int mx, int my, boolean erase) {
-        String s = String.format("       %10s   %10s      %10s", "", "", "");
-        if (!erase) {
-            rayTraceComputer.displayToSpaceOffset(mx, my, dx, dy);
-            String sx = dispRoundNum(dx.v);
-            String sy = dispRoundNum(dy.v);
-            String sz = dispRoundNum(parent.sv_dispScale);
-            s = String.format("Pos: X:%10s Y:%10s Zoom:%10s", sx, sy, sz);
-        }
-        parent.statusLabel.setText(s);
-    }
-
-    String dispRoundNum(double v) {
-        String result;
-        double av = Math.abs(v);
-        if (av > 100 || av < .1) {
-            result = String.format("%4.3e", v);
-        } else {
-            result = String.format("%4.3f", v);
-        }
-        return result;
-    }
-
-    public boolean hasMouse() {
-        return mouseInside;
-    }
-
-    void setMouseInside(boolean inside) {
-        mouseInside = inside;
-        if (!inside) {
-            updateStatusBar(0, 0, true);
-        }
-        parent.mouse_present_test(inside);
-    }
-
-    void updateDisplay() {
-        if (isVisible()) {
-            parent.controlPanelManager.writeLensValues(parent.selectedLens);
-            rayTraceProcess(true);
-        }
-    }
-
-    void handleMouseMove(MouseEvent evt) {
-        int mx = evt.getX();
-        int my = evt.getY();
-        rayTraceComputer.displayToSpace(mx, my, dx, dy);
-        if (testMouseInsideLens(dx.v + parent.sv_xOffset, dy.v + parent.sv_yOffset, true) != null) {
-            this.setCursor(handCursor);
-        } else {
-            this.setCursor(defaultCursor);
-        }
-        updateStatusBar(evt.getX(), evt.getY(), false);
-    }
-
-    void handleMouseDrag(MouseEvent evt) {
-        updateStatusBar(evt.getX(), evt.getY(), false);
-        int mx = evt.getX();
-        int my = evt.getY();
-        rayTraceComputer.displayToSpace(mx, my, dx, dy);
-        //Lens p = testMouseInsideLens(dx.v + parent.xOffset, dy.v + parent.yOffset);
-        if (parent.mouseTarget == null) {
-            parent.sv_xOffset = -dx.v + parent.mousePressX;
-            parent.sv_yOffset = -dy.v + parent.mousePressY;
-        } else {
-            parent.selectedLens.cx = dx.v + parent.mousePressX;
-            parent.selectedLens.cy = dy.v + parent.mousePressY;
-        }
-        rayTraceProcess(true);
-    }
-
-    void handleMouseWheelEvent(MouseWheelEvent evt) {
-        int mx = evt.getX();
-        int my = evt.getY();
-        double v = evt.getWheelRotation() * 0.1;
-        v = (evt.isShiftDown()) ? v * 0.1 : v;
-        v = (evt.isAltDown()) ? v * 0.01 : v;
-        parent.sv_dispScale *= 1.0 - v;
-        rayTraceProcess(true);
-        updateStatusBar(mx, my, false);
-    }
-
-    void handleMousePressEvent(MouseEvent evt) {
-        parent.pushUndo();
-        boolean isPopup = evt.isPopupTrigger();
-        this.setCursor(moveCursor);
-        int mx = evt.getX();
-        int my = evt.getY();
-        rayTraceComputer.displayToSpace(mx, my, dx, dy);
-        Lens p = testMouseInsideLens(dx.v + parent.sv_xOffset, dy.v + parent.sv_yOffset, isPopup);
-        if (p == null) {
-            parent.mousePressX = dx.v + parent.sv_xOffset;
-            parent.mousePressY = dy.v + parent.sv_yOffset;
-        } else {
-            parent.mousePressX = -dx.v + p.cx;
-            parent.mousePressY = -dy.v + p.cy;
-            parent.setSelectedLens(p);
-            rayTraceProcess(true);
-        }
-        parent.mouseTarget = p;
-        if (isPopup) {
-            this.setCursor(defaultCursor);
-            parent.popupMouseX = evt.getX();
-            parent.popupMouseY = evt.getY();
-            popupMenu.show(evt.getComponent(), parent.popupMouseX, parent.popupMouseY);
-        }
-    }
-
-    Lens testMouseInsideLens(double mx, double my, boolean isPopup) {
-        Vector<Lens> lensSet = new Vector<Lens>();
-        Iterator<Lens> it = parent.sv_lensList.iterator();
-        while (it.hasNext()) {
-            Lens lens = it.next();
-            if (lens.inside(mx, my)) {
-                lensSet.add(lens);
-            }
-        }
-        if (lensSet.size() == 0) {
-            return null;
-        } else {
-            // cycle between overlapped lenses
-            // as the user presses the mouse repeatedly
-            // but only if this is not a context-menu mouse press
-            int i = (isPopup) ? parent.overlappedLensSelector : ++parent.overlappedLensSelector;
-            return lensSet.get(i % lensSet.size());
-        }
-    }
-
-    // force lens to Y baseline
-    void snapLens(Lens lens) {
-        if (Math.abs(lens.cy) <= parent.ySnap) {
-            lens.cy = 0;
-        }
-    }
-
-    void handleMouseReleaseEvent(MouseEvent evt) {
-        this.setCursor(defaultCursor);
-        if (parent.selectedLens != null) {
-            snapLens(parent.selectedLens);
-            parent.controlPanelManager.readLensValues(parent.selectedLens);
-        }
-        rayTraceProcess(true);
-        if (evt.isPopupTrigger()) {
-            parent.popupMouseX = evt.getX();
-            parent.popupMouseY = evt.getY();
-            popupMenu.show(evt.getComponent(), parent.popupMouseX, parent.popupMouseY);
-        }
-    }
-
-    /** This method is called from within the constructor to
-     * initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is
-     * always regenerated by the Form Editor.
-     */
-    @SuppressWarnings("unchecked")
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-
-        popupMenu = new javax.swing.JPopupMenu();
-        newLensMenuItem = new javax.swing.JMenuItem();
-        cutMenuItem = new javax.swing.JMenuItem();
-        copyMenuItem = new javax.swing.JMenuItem();
-        pasteCursorMenuItem = new javax.swing.JMenuItem();
-        pasteAbsMenuItem = new javax.swing.JMenuItem();
-        deleteMenuItem = new javax.swing.JMenuItem();
-
-        newLensMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/document-new.png"))); // NOI18N
-        newLensMenuItem.setText("New Lens");
-        newLensMenuItem.setToolTipText("Create new lens at cursor position");
-        newLensMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseReleased(java.awt.event.MouseEvent evt) {
-                newLensMenuItemMouseReleased(evt);
-            }
-        });
-        popupMenu.add(newLensMenuItem);
-
-        cutMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-cut.png"))); // NOI18N
-        cutMenuItem.setText("Cut");
-        cutMenuItem.setToolTipText("Cut Selected Lens");
-        cutMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseReleased(java.awt.event.MouseEvent evt) {
-                cutMenuItemMouseReleased(evt);
-            }
-        });
-        popupMenu.add(cutMenuItem);
-
-        copyMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-copy.png"))); // NOI18N
-        copyMenuItem.setText("Copy");
-        copyMenuItem.setToolTipText("Copy Selected Lens");
-        copyMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseReleased(java.awt.event.MouseEvent evt) {
-                copyMenuItemMouseReleased(evt);
-            }
-        });
-        popupMenu.add(copyMenuItem);
-
-        pasteCursorMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-paste.png"))); // NOI18N
-        pasteCursorMenuItem.setText("Paste: mouse cursor");
-        pasteCursorMenuItem.setToolTipText("Paste lens to mouse cursor position");
-        pasteCursorMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseReleased(java.awt.event.MouseEvent evt) {
-                pasteCursorMenuItemMouseReleased(evt);
-            }
-        });
-        popupMenu.add(pasteCursorMenuItem);
-
-        pasteAbsMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-paste.png"))); // NOI18N
-        pasteAbsMenuItem.setText("Paste: defined position");
-        pasteAbsMenuItem.setToolTipText("Paste lens to its defined position");
-        pasteAbsMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseReleased(java.awt.event.MouseEvent evt) {
-                pasteAbsMenuItemMouseReleased(evt);
-            }
-        });
-        popupMenu.add(pasteAbsMenuItem);
-
-        deleteMenuItem.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/process-stop.png"))); // NOI18N
-        deleteMenuItem.setText("Delete");
-        deleteMenuItem.setToolTipText("Delete Selected Lens");
-        deleteMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseReleased(java.awt.event.MouseEvent evt) {
-                deleteMenuItemMouseReleased(evt);
-            }
-        });
-        popupMenu.add(deleteMenuItem);
-
-        setBackground(java.awt.Color.white);
-        setToolTipText("<html>\nZoom = mouse wheel<br/>\nPan = drag mouse<br/>\n</html>");
-        addMouseWheelListener(new java.awt.event.MouseWheelListener() {
-            public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
-                formMouseWheelMoved(evt);
-            }
-        });
-        addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseEntered(java.awt.event.MouseEvent evt) {
-                formMouseEntered(evt);
-            }
-            public void mouseExited(java.awt.event.MouseEvent evt) {
-                formMouseExited(evt);
-            }
-            public void mousePressed(java.awt.event.MouseEvent evt) {
-                formMousePressed(evt);
-            }
-            public void mouseReleased(java.awt.event.MouseEvent evt) {
-                formMouseReleased(evt);
-            }
-        });
-        addComponentListener(new java.awt.event.ComponentAdapter() {
-            public void componentResized(java.awt.event.ComponentEvent evt) {
-                formComponentResized(evt);
-            }
-        });
-        addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
-            public void mouseDragged(java.awt.event.MouseEvent evt) {
-                formMouseDragged(evt);
-            }
-            public void mouseMoved(java.awt.event.MouseEvent evt) {
-                formMouseMoved(evt);
-            }
-        });
-
-        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
-        this.setLayout(layout);
-        layout.setHorizontalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGap(0, 400, Short.MAX_VALUE)
-        );
-        layout.setVerticalGroup(
-            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGap(0, 300, Short.MAX_VALUE)
-        );
-    }// </editor-fold>//GEN-END:initComponents
-
-    private void formMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseEntered
-        // TODO add your handling code here:
-        setMouseInside(true);
-    }//GEN-LAST:event_formMouseEntered
-
-    private void formMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseExited
-        // TODO add your handling code here:
-        setMouseInside(false);
-    }//GEN-LAST:event_formMouseExited
-
-    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
-        // TODO add your handling code here:
-        repaint();
-    }//GEN-LAST:event_formComponentResized
-
-    private void formMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseMoved
-        // TODO add your handling code here:
-        handleMouseMove(evt);
-    }//GEN-LAST:event_formMouseMoved
-
-    private void formMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_formMouseWheelMoved
-        // TODO add your handling code here:
-        handleMouseWheelEvent(evt);
-    }//GEN-LAST:event_formMouseWheelMoved
-
-    private void formMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMousePressed
-        // TODO add your handling code here:
-        handleMousePressEvent(evt);
-    }//GEN-LAST:event_formMousePressed
-
-    private void formMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseReleased
-        // TODO add your handling code here:
-        handleMouseReleaseEvent(evt);
-    }//GEN-LAST:event_formMouseReleased
-
-    private void formMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseDragged
-        // TODO add your handling code here:
-        handleMouseDrag(evt);
-    }//GEN-LAST:event_formMouseDragged
-
-    private void cutMenuItemMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cutMenuItemMouseReleased
-        // TODO add your handling code here:
-        parent.clipboardCutLens();
-    }//GEN-LAST:event_cutMenuItemMouseReleased
-
-    private void copyMenuItemMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_copyMenuItemMouseReleased
-        // TODO add your handling code here:
-        parent.clipboardCopyLens();
-    }//GEN-LAST:event_copyMenuItemMouseReleased
-
-    private void pasteCursorMenuItemMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_pasteCursorMenuItemMouseReleased
-        // TODO add your handling code here:
-        parent.clipboardPasteLens(true);
-}//GEN-LAST:event_pasteCursorMenuItemMouseReleased
-
-    private void newLensMenuItemMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_newLensMenuItemMouseReleased
-        // TODO add your handling code here:
-        parent.makeNewLensPopup();
-    }//GEN-LAST:event_newLensMenuItemMouseReleased
-
-    private void deleteMenuItemMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_deleteMenuItemMouseReleased
-        // TODO add your handling code here:
-        parent.deleteSelectedLens();
-    }//GEN-LAST:event_deleteMenuItemMouseReleased
-
-    private void pasteAbsMenuItemMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_pasteAbsMenuItemMouseReleased
-        // TODO add your handling code here:
-        parent.clipboardPasteLens(false);
-}//GEN-LAST:event_pasteAbsMenuItemMouseReleased
-
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JMenuItem copyMenuItem;
-    private javax.swing.JMenuItem cutMenuItem;
-    private javax.swing.JMenuItem deleteMenuItem;
-    private javax.swing.JMenuItem newLensMenuItem;
-    private javax.swing.JMenuItem pasteAbsMenuItem;
-    private javax.swing.JMenuItem pasteCursorMenuItem;
-    private javax.swing.JPopupMenu popupMenu;
-    // End of variables declaration//GEN-END:variables
+import static java.lang.Math.abs;
+
+import java.awt.AWTException;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.Robot;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionAdapter;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+
+import javax.swing.ImageIcon;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+
+ at SuppressWarnings("serial")
+final public class GraphicDisplay extends JPanel {
+
+	boolean mouseInside = false;
+	OpticalRayTracer parent;
+	OpticalComponent ocUnderMouse = null;
+	String name;
+	int tabValue;
+	int testCount = 0;
+	ProgramValues programValues;
+	RayTraceComputer rayTraceComputer;
+	JPopupMenu popupMenu;
+	boolean hasFocus = false;
+	
+	Cursor handCursor, moveCursor, crossCursor, defaultCursor;
+
+	boolean shiftKey, ctrlKey, altKey;
+
+	public GraphicDisplay(OpticalRayTracer p, String name, int tab) {
+		this.name = name;
+		tabValue = tab;
+		addFocusListener(new FocusAdapter() {
+			@Override
+			public void focusGained(FocusEvent e) {
+				hasFocus = true;
+				repaint();
+			}
+			@Override
+			public void focusLost(FocusEvent e) {
+				hasFocus = false;
+				repaint();
+			}
+		});
+		addMouseWheelListener(new MouseWheelListener() {
+			public void mouseWheelMoved(MouseWheelEvent e) {
+				handleMouseWheelEvent(e);
+			}
+		});
+		addMouseMotionListener(new MouseMotionAdapter() {
+			@Override
+			public void mouseMoved(MouseEvent e) {
+				handleMouseMove(e);
+			}
+
+			@Override
+			public void mouseDragged(MouseEvent e) {
+				handleMouseDrag(e);
+			}
+		});
+		addComponentListener(new ComponentAdapter() {
+			@Override
+			public void componentResized(ComponentEvent e) {
+				repaint();
+			}
+		});
+		addMouseListener(new MouseAdapter() {
+			@Override
+			public void mousePressed(MouseEvent e) {
+				handleMousePressEvent(e);
+			}
+
+			@Override
+			public void mouseEntered(MouseEvent e) {
+				setMouseInside(true);
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				setMouseInside(false);
+			}
+
+			@Override
+			public void mouseReleased(MouseEvent e) {
+				handleMouseReleaseEvent(e);
+			}
+		});
+		// this allows the tab key to be captured
+		// setFocusTraversalKeysEnabled(false);
+		addKeyListener(new KeyListener() {
+
+			@Override
+			public void keyTyped(KeyEvent e) {
+				// TODO Auto-generated method stub
+				// parent.p("key typed");
+			}
+
+			@Override
+			public void keyPressed(KeyEvent e) {
+				// TODO Auto-generated method stub
+				// parent.p("key pressed");
+				handleKeyPressed(e);
+			}
+
+			@Override
+			public void keyReleased(KeyEvent e) {
+				// TODO Auto-generated method stub
+				// parent.p("key released");
+
+			}
+		});
+
+		parent = p;
+		programValues = parent.programValues;
+		rayTraceComputer = parent.rayTraceComputer;
+		handCursor = new Cursor(Cursor.HAND_CURSOR);
+		moveCursor = new Cursor(Cursor.MOVE_CURSOR);
+		crossCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
+		defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
+		// initComponents();
+		// setFocusable(true);
+		// setRequestFocusEnabled(true);
+
+		popupMenu = new JPopupMenu();
+		// popupMenu.setLightWeightPopupEnabled(false);
+		// popupMenu.setInvoker(this);
+		addPopup(this, popupMenu);
+
+		JMenuItem mntmNewLens = new JMenuItem("New Lens");
+		mntmNewLens.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.makeNewLensPopup(parent.popupMouseX, parent.popupMouseY);
+			}
+		});
+		mntmNewLens.setToolTipText("Create new lens at cursor position");
+		mntmNewLens.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/document-new.png")));
+		popupMenu.add(mntmNewLens);
+
+		JMenuItem mntmNewMirror = new JMenuItem("New Mirror");
+		mntmNewMirror.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.makeNewMirrorPopup(parent.popupMouseX,
+						parent.popupMouseY);
+			}
+		});
+		mntmNewMirror.setToolTipText("Create new mirror at cursor position");
+		mntmNewMirror.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/view-fullscreen.png")));
+		popupMenu.add(mntmNewMirror);
+
+		JMenuItem mntmLineProperties = new JMenuItem("Line Properties");
+		mntmLineProperties.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.lineAnalysis.nearestLineProperties(parent.mousePressX,
+						parent.mousePressY);
+			}
+		});
+		mntmLineProperties
+				.setToolTipText("<html>List properties of line closest to mouse cursor<br/>(double-click also works)");
+
+		mntmLineProperties.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/document-save.png")));
+		popupMenu.add(mntmLineProperties);
+
+		JMenuItem mntmCut = new JMenuItem("Cut");
+		mntmCut.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.clipboardCutLens();
+			}
+		});
+		mntmCut.setToolTipText("Cut selected object");
+		mntmCut.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/edit-cut.png")));
+		popupMenu.add(mntmCut);
+
+		JMenuItem mntmCopy = new JMenuItem("Copy");
+		mntmCopy.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.clipboardCopyLens();
+			}
+		});
+		mntmCopy.setToolTipText("Copy selected object");
+		mntmCopy.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/edit-copy.png")));
+		popupMenu.add(mntmCopy);
+
+		JMenuItem mntmPaste = new JMenuItem("Paste: mouse cursor");
+		mntmPaste.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.clipboardPasteObject(true);
+			}
+		});
+		mntmPaste.setToolTipText("Paste object to mouse cursor position");
+		mntmPaste.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/edit-paste.png")));
+		popupMenu.add(mntmPaste);
+
+		JMenuItem mntmNewMenuItem = new JMenuItem("Paste: defined position");
+		mntmNewMenuItem.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.clipboardPasteObject(false);
+			}
+		});
+		mntmNewMenuItem.setToolTipText("Paste object to its defined position");
+		mntmNewMenuItem.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/edit-paste.png")));
+		popupMenu.add(mntmNewMenuItem);
+
+		JMenuItem mntmDelete = new JMenuItem("Delete");
+		mntmDelete.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				parent.deleteSelectedLens();
+			}
+		});
+		mntmDelete.setToolTipText("Delete selected object");
+		mntmDelete.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/process-stop.png")));
+		popupMenu.add(mntmDelete);
+
+		JMenuItem mntmContextHelp = new JMenuItem("Context Help");
+		mntmContextHelp.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				contextHelp();
+			}
+		});
+		mntmContextHelp
+				.setToolTipText("Show a brief explanation of this display's controls");
+
+		mntmContextHelp.setIcon(new ImageIcon(GraphicDisplay.class
+				.getResource("/opticalraytracer/icons/system-help.png")));
+		popupMenu.add(mntmContextHelp);
+	}
+
+	protected void acquireFocus() {
+		setFocusable(true);
+		setRequestFocusEnabled(true);
+		requestFocusInWindow();
+	}
+
+	protected Vector setupCursorPosition(int centerX, int centerY) {
+		parent.popupMouseX = centerX;
+		parent.popupMouseY = centerY;
+		Vector p = parent.displayToSpace(parent.popupMouseX, parent.popupMouseY);
+		parent.mousePressX = p.x + programValues.xOffset;
+		parent.mousePressY = p.y + programValues.yOffset;
+		return p;
+	}
+	
+	protected void centerCursorOnScreen() {
+		int centerX = getWidth() / 2;
+		int centerY = getHeight() / 2;
+		Point ps = getLocationOnScreen();
+		// this.setCursor(crossCursor);
+		try {
+			Robot r = new Robot();
+			r.mouseMove(centerX + ps.x, centerY + ps.y);
+		} catch (AWTException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	private void handleKeyPressed(KeyEvent evt) {
+		int centerX = getWidth() / 2;
+		int centerY = getHeight() / 2;
+		boolean ctrlKey = evt.isControlDown();
+		boolean shiftKey = evt.isShiftDown();
+		boolean altKey = evt.isAltDown();
+		double multiplier = 1;
+		multiplier = (shiftKey) ? multiplier * 0.1 : multiplier;
+		multiplier = (altKey) ? multiplier * 0.1 : multiplier;
+
+		double panStep = .05 * multiplier / programValues.dispScale;
+		double zoomStep = .1 * multiplier;
+		boolean consume = true;
+
+		int kcode = evt.getKeyCode();
+		switch (kcode) {
+		case KeyEvent.VK_F1:
+			contextHelp();
+			break;
+		case KeyEvent.VK_ENTER:
+			Vector c = parent.displayToSpace(centerX, centerY).translate(programValues.xOffset,programValues.yOffset);
+			//c.translate(programValues.xOffset,programValues.yOffset);
+			OpticalComponent p = testMouseInsideLens(c,false);
+			if (p != null) {
+				parent.setSelectedComponent(p);
+				parent.mouseTarget = p;
+			} else {
+				setupCursorPosition(centerX, centerY);
+				parent.lineAnalysis.nearestLineProperties(parent.mousePressX,
+						parent.mousePressY);
+			}
+			break;
+		case KeyEvent.VK_L:
+			setupCursorPosition(centerX, centerY);
+			parent.lineAnalysis.nearestLineProperties(parent.mousePressX,
+					parent.mousePressY);
+			break;
+		case KeyEvent.VK_O:
+			parent.selectNextObject();
+			break;
+		case KeyEvent.VK_U:
+			parent.unSelectLens();
+			break;
+		case KeyEvent.VK_M:
+		case KeyEvent.VK_CONTEXT_MENU:
+		case KeyEvent.VK_WINDOWS:
+			setupCursorPosition(centerX, centerY);
+			popupMenu.show(this, parent.popupMouseX, parent.popupMouseY);
+			break;
+		case KeyEvent.VK_TAB:
+			// break out to allow keys to work in the remainder
+			// of the program including tab itself
+			break;
+		case KeyEvent.VK_NUMPAD7:
+		case KeyEvent.VK_HOME:
+		case KeyEvent.VK_ADD:
+		case KeyEvent.VK_PLUS:
+		case KeyEvent.VK_EQUALS:
+			processZoom(ctrlKey, shiftKey, zoomStep);
+			break;
+		case KeyEvent.VK_NUMPAD1:
+		case KeyEvent.VK_SUBTRACT:
+		case KeyEvent.VK_END:
+		case KeyEvent.VK_MINUS:
+			processZoom(ctrlKey, shiftKey, -zoomStep);
+			break;
+		case KeyEvent.VK_NUMPAD8:
+		case KeyEvent.VK_KP_UP:
+		case KeyEvent.VK_UP:
+			processPan(true, ctrlKey, panStep);
+			break;
+		case KeyEvent.VK_NUMPAD2:
+		case KeyEvent.VK_KP_DOWN:
+		case KeyEvent.VK_DOWN:
+			processPan(true, ctrlKey, -panStep);
+			break;
+		case KeyEvent.VK_NUMPAD4:
+		case KeyEvent.VK_LEFT:
+		case KeyEvent.VK_KP_LEFT:
+			processPan(false, ctrlKey, -panStep);
+			break;
+		case KeyEvent.VK_NUMPAD6:
+		case KeyEvent.VK_RIGHT:
+		case KeyEvent.VK_KP_RIGHT:
+			processPan(false, ctrlKey, panStep);
+			break;
+		default:
+			consume = false;
+			break;
+		}
+		if (consume) {
+			evt.consume();
+			centerCursorOnScreen();
+			rayTraceProcess(true);
+		}
+	}
+
+	protected void processPan(boolean vertical, boolean ctrlKey, double step) {
+		if (ctrlKey) {
+			OpticalComponent oc = parent.selectedComponent;
+			if (oc != null) {
+				// move the object and pan the view simultaneously
+				if (vertical) {
+					oc.values.yPos += step;
+					parent.programValues.yOffset += step;
+				} else {
+					oc.values.xPos += step;
+					parent.programValues.xOffset += step;
+				}
+				oc.writeObjectControls();
+			}
+		} else {
+			// just pan the view
+			if (vertical) {
+				programValues.yOffset += step;
+			} else {
+				programValues.xOffset += step;
+			}
+		}
+	}
+
+	protected void processZoom(boolean ctrlKey, boolean shiftKey, double step) {
+		OpticalComponent oc = parent.selectedComponent;
+		if (shiftKey && oc != null) {
+			oc.values.angle = (oc.values.angle + (step * 100 + 720)) % 360;
+			oc.writeObjectControls();
+		} else if (ctrlKey && oc != null) {
+			oc.values.lensRadius += step;
+			oc.writeObjectControls();
+		} else {
+			programValues.dispScale *= (1 + step);
+		}
+	}
+
+	@Override
+	public void paintComponent(Graphics g) {
+		//parent.p("paintcomponent: " + name + " " + testCount);
+		//testCount += 1;
+		int w = getWidth();
+		int h = getHeight();
+		rayTraceProcessCore(w, h,false);
+		g.drawImage(parent.image, 0, 0, null);
+	}
+
+	// allows drawing any size
+	void drawData(Graphics g, int x, int y) {
+		rayTraceProcessCore(x, y,false);
+		g.drawImage(parent.image, 0, 0, null);
+	}
+
+	void rayTraceProcess(boolean paint) {
+		if (!paint || parent.currentTab() == tabValue) {
+			// parent.p("repainting: " + name + " " + testCount);
+			// testCount += 1;
+			int w = getWidth();
+			int h = getHeight();
+			if (paint) {
+				repaint();
+			} else {
+				rayTraceProcessCore(w, h,false);
+			}
+		}
+	}
+
+	void rayTraceProcessCore(int w, int h, boolean forceFocus) {
+		// parent.p("raytraceprocesscore: " + name);
+		if (updateGraphicBuffer(w, h)) {
+			parent.unselectButton.setEnabled(parent.selectedComponent != null);
+			Graphics2D bg = (Graphics2D) parent.image.getGraphics();
+			if (programValues.antialias) {
+				RenderingHints rh = new RenderingHints(
+						RenderingHints.KEY_ANTIALIASING,
+						RenderingHints.VALUE_ANTIALIAS_ON);
+				bg.addRenderingHints(rh);
+			}
+			Color bgColor = (hasFocus || forceFocus)?new Color(
+					programValues.inverse ? programValues.colorLowBackground
+							: programValues.colorHighBackground):
+								programValues.inverse ?Common.noFocusInverse:
+								Common.noFocusHi;
+			bg.setColor(bgColor);
+			bg.fillRect(0, 0, parent.image.getWidth(), parent.image.getHeight());
+			if (programValues.beamWidth > 1) {
+				bg.setStroke(new BasicStroke((int) programValues.beamWidth));
+			}
+			if (programValues.showGrid) {
+				rayTraceComputer.drawGrid(bg);
+				rayTraceComputer.drawBaselines(bg);
+			}
+			rayTraceComputer.drawLenses(bg);
+			rayTraceComputer.traceRays(bg, false);
+			bg.dispose();
+		}
+	}
+
+	boolean updateGraphicBuffer(int x, int y) {
+		boolean success = false;
+		if (x > 0 && y > 0) {
+			success = true;
+			if (parent.image == null || parent.xSize != x || parent.ySize != y) {
+				parent.xSize = x;
+				parent.ySize = y;
+				parent.image = new BufferedImage(x, y,
+						BufferedImage.TYPE_INT_RGB);
+				parent.xCenter = parent.xSize / 2;
+				parent.yCenter = parent.ySize / 2;
+			}
+		}
+		return success;
+	}
+
+	void updateStatusBar(int mx, int my, boolean erase) {
+		String s = String.format("       %10s   %10s      %10s", "", "", "");
+		if (!erase) {
+			Vector sp = parent.displayToSpaceOffset(new Vector(mx, my));
+			String sx = dispRoundNum(sp.x);
+			String sy = dispRoundNum(sp.y);
+			String sz = dispRoundNum(programValues.dispScale);
+			s = String.format("Pos: X:%10s Y:%10s Magnification:%10s", sx, sy, sz);
+		}
+		parent.statusLabel.setText(s);
+	}
+
+	String dispRoundNum(double v) {
+		String result;
+		double av = abs(v);
+		if (av > 100 || av < .1) {
+			result = String.format("%9.4e", v);
+		} else {
+			result = String.format("%9.4f", v);
+		}
+		return result;
+	}
+
+	public boolean hasMouse() {
+		return mouseInside;
+	}
+
+	void setMouseInside(boolean inside) {
+		mouseInside = inside;
+		if (!inside) {
+			updateStatusBar(0, 0, true);
+		}
+	}
+
+	void updateDisplay() {
+		rayTraceProcess(true);
+	}
+
+	void handleMouseMove(MouseEvent evt) {
+		int mx = evt.getX();
+		int my = evt.getY();
+		Vector p = parent.displayToSpace(mx, my).translate(programValues.xOffset,programValues.yOffset );
+		//p.translate(programValues.xOffset,programValues.yOffset );
+		//double sx = dx.v + programValues.xOffset;
+	//	double sy = dy.v + programValues.yOffset;
+		if (testMouseInsideLens(p, true) != null) {
+			this.setCursor(handCursor);
+		} else {
+			this.setCursor(defaultCursor);
+		}
+		updateStatusBar(evt.getX(), evt.getY(), false);
+		StringBuilder sb = new StringBuilder();
+		if (ocUnderMouse != null) {
+			sb.append(ocUnderMouse.values.name);
+			sb.append(" ");
+		}
+		sb.append(String.format("{%s,%s}", parent.formatNum(p.x),
+				parent.formatNum(p.y)));
+		setToolTipText(sb.toString());
+	}
+
+	void handleMouseDrag(MouseEvent evt) {
+		updateStatusBar(evt.getX(), evt.getY(), false);
+		int mx = evt.getX();
+		int my = evt.getY();
+		Vector p = parent.displayToSpace(mx, my);
+		if (!(shiftKey || ctrlKey)) {
+			programValues.xOffset = -p.x + parent.mousePressX;
+			programValues.yOffset = -p.y + parent.mousePressY;
+		} else if (parent.selectedComponent != null && (shiftKey || ctrlKey)) {
+			parent.selectedComponent.values.xPos = p.x + parent.mousePressX;
+			parent.selectedComponent.values.yPos = p.y + parent.mousePressY;
+			parent.selectedComponent.writeObjectControls();
+		}
+
+		rayTraceProcess(true);
+	}
+
+	void detectKeys(MouseEvent evt) {
+		shiftKey = (evt.getModifiers() & InputEvent.SHIFT_MASK) != 0;
+		ctrlKey = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0;
+		altKey = (evt.getModifiers() & InputEvent.ALT_MASK) != 0;
+	}
+
+	void handleMouseWheelEvent(MouseWheelEvent evt) {
+		int mx = evt.getX();
+		int my = evt.getY();
+		detectKeys(evt);
+		double v = evt.getWheelRotation();
+		double mv = v * ((altKey) ? 1 : 5);
+		if (parent.selectedComponent != null) {
+			if (shiftKey) {
+				parent.selectedComponent.values.angle = (parent.selectedComponent.values.angle + (mv + 720)) % 360;
+				parent.selectedComponent.writeObjectControls();
+			} else if (ctrlKey) {
+				parent.selectedComponent.values.lensRadius -= mv * .01;
+				parent.selectedComponent.writeObjectControls();
+			} else {
+				programValues.dispScale *= 1 - mv * .02;
+			}
+
+		} else {
+			programValues.dispScale *= 1 - mv * .02;
+		}
+
+		rayTraceProcess(true);
+		updateStatusBar(mx, my, false);
+		evt.consume();
+	}
+
+	void handleMousePressEvent(MouseEvent evt) {
+		parent.undoPush();
+		requestFocus();
+		boolean doubleClick = evt.getClickCount() == 2;
+		boolean isPopup = evt.isPopupTrigger();
+		this.setCursor(moveCursor);
+		int mx = evt.getX();
+		int my = evt.getY();
+		detectKeys(evt);
+		Vector op = parent.displayToSpace(mx, my);
+		Vector offset = op.translate(programValues.xOffset,programValues.yOffset);
+		//offset.translate(programValues.xOffset,programValues.yOffset);
+		OpticalComponent p = testMouseInsideLens(offset, isPopup);
+		if (p != null) {
+			//parent.p("clicked with xPos1: " + p.values.xPos);
+			parent.setSelectedComponent(p);
+			//parent.p("clicked with xPos2: " + p.values.xPos);
+			parent.mouseTarget = p;
+		}
+		if (!(shiftKey || ctrlKey)) {
+			parent.mousePressX = op.x + programValues.xOffset;
+			parent.mousePressY = op.y + programValues.yOffset;
+		} else if (p != null) {
+			if (ctrlKey || shiftKey) {
+				parent.mousePressX = -op.x + p.values.xPos;
+				parent.mousePressY = -op.y + p.values.yPos;
+			}
+			rayTraceProcess(true);
+		}
+		if (!(shiftKey || ctrlKey || altKey)) {
+			if (isPopup && isVisible()) {
+				this.setCursor(defaultCursor);
+				parent.popupMouseX = evt.getX();
+				parent.popupMouseY = evt.getY();
+			}
+			if (doubleClick) {
+				parent.lineAnalysis.nearestLineProperties(parent.mousePressX,
+						parent.mousePressY);
+			}
+		}
+	}
+
+	OpticalComponent testMouseInsideLens(Vector mp, boolean isPopup) {
+		ArrayList<OpticalComponent> lensSet = new ArrayList<OpticalComponent>();
+		for (OpticalComponent oc : parent.componentList) {
+			if (oc.inside(mp, oc.mouseProximityPolygon)) {
+				lensSet.add(oc);
+			}
+		}
+		if (lensSet.size() == 0) {
+			ocUnderMouse = null;
+			return null;
+		} else {
+			// cycle between overlapped lenses
+			// as the user presses the mouse repeatedly
+			// but only if this is not a context-menu mouse press
+			// and only if no modifier keys are pressed
+			if(!(isPopup | shiftKey | ctrlKey | altKey)) {
+				parent.overlappedLensSelector += 1;
+			}
+			ocUnderMouse = lensSet.get(parent.overlappedLensSelector % lensSet.size());
+			return ocUnderMouse;
+		}
+	}
+
+	
+	void handleMouseReleaseEvent(MouseEvent evt) {
+		this.setCursor(defaultCursor);
+		if (parent.selectedComponent != null) {
+			parent.selectedComponent.snapToGrid();
+			if (parent.selectedComponent != null) {
+				parent.selectedComponent.writeObjectControls();
+			}
+		}
+		rayTraceProcess(true);
+		if (evt.isPopupTrigger()) {
+			parent.popupMouseX = evt.getX();
+			parent.popupMouseY = evt.getY();
+		}
+	}
+
+	protected void contextHelp() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("Mouse related:\n");
+		sb.append("  Click once: select object\n");
+		sb.append("  Click more: cycle through overlapping objects\n");
+		sb.append("  Double-click: list properties of nearest line\n");
+		sb.append("  Drag mouse: pan display\n");
+		sb.append("  Drag mouse with Shift or Ctrl key: move selected object\n");
+		sb.append("  Mouse wheel: zoom display\n");
+		sb.append("  Mouse wheel with Shift key: rotate selected object\n");
+		sb.append("  Mouse wheel with Ctrl key: rescale selected object\n");
+		sb.append("Keyboard related:\n");
+		sb.append("  Tab: Move forward through all program controls\n");
+		sb.append("  Shift|Tab: Move in reverse through all program controls\n");
+		sb.append("  Alt-D: Design tab\n");
+		sb.append("  Alt-C: Configure tab\n");
+		sb.append("  Alt-T: Table tab\n");
+		sb.append("  Alt-H: Help tab\n");
+		sb.append("  F1: Concise help dialog (this dialog)\n");
+		sb.append("  M or Context-menu key: context menu\n");
+		sb.append("  Enter (over object): select object under cursor\n");
+		sb.append("  Enter (outside objects): List properties of nearest line\n");
+		sb.append("  L: [L]ist properties of nearest line (even inside objects)\n");
+		sb.append("  U: [U]nselect all objects\n");
+		sb.append("  O: Cycle through [O]bject selections\n");
+		sb.append("  Up/down/left/right arrow keys: pan display\n");
+		sb.append("  Ctrl|Arrow or Shift|Arrow keys: move selected object\n");
+		sb.append("  +/- or Home/End: zoom display in/out\n");
+		sb.append("  Ctrl|(+/-) or Ctrl|(Home/End): resize selected object\n");
+		sb.append("  Shift|(+/-) or Shift|(Home/End): rotate selected object\n");
+		sb.append("Most of the above with Alt key: slower change\n");
+		sb.append("This information is also in the Help file, under\n\"Using the mouse and keyboard\".");
+		parent.showNotifyMessageFormatted(sb.toString(), "Context help");
+	}
+
+	private static void addPopup(Component component, final JPopupMenu popup) {
+		component.addMouseListener(new MouseAdapter() {
+			public void mousePressed(MouseEvent e) {
+				if (e.isPopupTrigger()) {
+					showMenu(e);
+				}
+			}
+
+			public void mouseReleased(MouseEvent e) {
+				if (e.isPopupTrigger()) {
+					showMenu(e);
+				}
+			}
+
+			private void showMenu(MouseEvent e) {
+				try {
+					if (e.getComponent().isVisible()) {
+						popup.show(e.getComponent(), e.getX(), e.getY());
+					}
+				} catch (Exception ex) {
+				}
+			}
+		});
+	}
 }
diff --git a/src/opticalraytracer/HelpState.java b/src/opticalraytracer/HelpState.java
new file mode 100644
index 0000000..37ddea3
--- /dev/null
+++ b/src/opticalraytracer/HelpState.java
@@ -0,0 +1,13 @@
+package opticalraytracer;
+
+final public class HelpState {
+	int scrollBar = 0;
+	int selectStart = 0;
+	int selectEnd = 0;
+
+	public HelpState(int sb, int ss, int se) {
+		scrollBar = sb;
+		selectStart = ss;
+		selectEnd = se;
+	}
+}
\ No newline at end of file
diff --git a/src/opticalraytracer/ImageTransferable.java b/src/opticalraytracer/ImageTransferable.java
index bc1b209..2bb1622 100644
--- a/src/opticalraytracer/ImageTransferable.java
+++ b/src/opticalraytracer/ImageTransferable.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,39 +17,13 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
+
 package opticalraytracer;
 
 import java.awt.*;
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.Transferable;
 
-/**
- *
- * @author lutusp
- */
 final public class ImageTransferable implements Transferable {
 
     Image image;
diff --git a/src/opticalraytracer/InitManager.java b/src/opticalraytracer/InitManager.java
deleted file mode 100644
index 1292e5d..0000000
--- a/src/opticalraytracer/InitManager.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-import java.lang.reflect.Field;
-import java.util.*;
-import java.awt.*;
-import java.io.*;
-import java.util.Scanner.*;
-import javax.swing.*;
-
-/**
- *
- * @author lutusp
- */
-final public class InitManager {
-
-    OpticalRayTracer parent;
-    String fileSep;
-    String lineSep;
-    String appName;
-    String userDir;
-    String userPath = "(user home directory)";
-    String initPath;
-    String fieldPrefix = "sv_";
-
-    public InitManager(OpticalRayTracer p, boolean isApplet) {
-        parent = p;
-        lineSep = System.getProperty("line.separator");
-        fileSep = System.getProperty("file.separator");
-        if (!isApplet) {
-            appName = parent.getClass().getSimpleName();
-            userDir = System.getProperty("user.home");
-            userPath = userDir + fileSep + "." + appName;
-            initPath = userPath + fileSep + appName + ".ini";
-            testMakeDirs(userPath);
-        }
-    }
-
-    String[] parsePair(String val) {
-        String pair[] = null;
-        int p = val.indexOf("=");
-        if (p > 0) {
-            pair = new String[]{val.substring(0, p), val.substring(p + 1, val.length())};
-            for (int i = 0; i < 2 && i < pair.length; i++) {
-                pair[i] = pair[i].replaceFirst("^\\s*(.*?)\\s*$", "$1");
-            }
-        }
-        return pair;
-    }
-
-    public void setConfig(String data) {
-        if (data != null) {
-            String values[] = data.split("\n|\r");
-            for (int i = 0; i < values.length; i++) {
-                String item = strip(values[i]);
-                item = item.replaceFirst("^\\s*#.*$", "");
-                // System.out.println(item + " (" + item.length() + ")");
-                if (item.length() > 0) {
-                    String[] pair = parsePair(item);
-                    if (pair != null && pair.length == 2) {
-                        readWriteField(pair[0], pair[1]);
-                    }
-                }
-            }
-        }
-    }
-
-    public void readConfig() {
-        String data = readTextFile(initPath, lineSep);
-        setConfig(data);
-    }
-
-    // parsing a rectangle'a textual representation
-    // is really quite a pain
-    Rectangle parseRectangle(String value) {
-        Vector<Integer> vi = new Vector<Integer>();
-        Rectangle rect = null;
-        try {
-            String s = value.replaceFirst("^.*x=([-|\\d]+).*y=([-|\\d]+).*width=([-|\\d]+).*height=([-|\\d]+).*$", "$1,$2,$3,$4");
-            String s_vals[] = s.split(",");
-            for (int j = 0; j < s_vals.length; j++) {
-                vi.add(Integer.parseInt(s_vals[j]));
-            }
-            Iterator<Integer> it = vi.iterator();
-            rect = new Rectangle(it.next(), it.next(), it.next(), it.next());
-        } catch (Exception e) {
-            System.out.println(getClass().getName() + ": error: " + e);
-        }
-        return rect;
-    }
-
-    String readWriteField(String name, String value) {
-        try {
-            Class pc = parent.getClass();
-            //System.out.println("looking for [" + pair[0] + "]");
-            Field f = pc.getDeclaredField(name);
-            Object obj = f.get(parent); // get the class instance
-            String classType = f.getType().toString();
-            //System.out.println("name: " + name + ", classtype: " + classType);
-            classType = classType.replaceFirst(".*\\.(.*)", "$1");
-            boolean write = (value != null);
-            if (write) {
-                value = strip(value);
-            }
-            if (classType.equals("int")) {
-                if (write) {
-                    f.setInt(parent, Integer.parseInt(value));
-                } else {
-                    value = "" + f.getInt(parent);
-                }
-            } else if (classType.equals("double")) {
-                if (write) {
-                    f.setDouble(parent, parent.getDouble(value));
-                } else {
-                    value = "" + parent.formatNum(f.getDouble(parent));
-                }
-            } else if (classType.equals("MutableDouble")) {
-                if (write) {
-                    ((MutableDouble) obj).v = parent.getDouble(value);
-                } else {
-                    value = "" + parent.formatNum(((MutableDouble) obj).v);
-                }
-            } else if (classType.equals("boolean")) {
-                if (write) {
-                    f.setBoolean(parent, value.equals("true"));
-                } else {
-                    value = "" + f.getBoolean(parent);
-                }
-            } else if (classType.equals("String")) {
-                if (write) {
-                    f.set(parent, value);
-                } else {
-                    value = (String) f.get(parent);
-                }
-            } else if (classType.equals("Rectangle")) {
-                if (write) {
-                    Rectangle r = parseRectangle(value);
-                    if (r != null) {
-                        f.set(parent, r);
-                    }
-                } else {
-                    value = (String) f.get(parent).toString();
-                }
-            } else if (classType.equals("JRadioButton")) {
-                JRadioButton button = (JRadioButton) obj;
-                if (write) {
-                    button.setSelected(value.equals("true"));
-                } else {
-                    value = "" + button.isSelected();
-                }
-            } else if (classType.equals("JCheckBox")) {
-                JCheckBox cb = (JCheckBox) obj;
-                if (write) {
-                    cb.setSelected(value.equals("true"));
-                } else {
-                    value = "" + cb.isSelected();
-                }
-            } else if (classType.equals("JTextField")) {
-                JTextField tf = (JTextField) obj;
-                if (write) {
-                    tf.setText(value);
-                } else {
-                    value = tf.getText();
-                }
-            } else if (classType.equals("JComboBox")) {
-                JComboBox box = (JComboBox) obj;
-                if (write) {
-                    box.setSelectedIndex(Integer.parseInt(value));
-                } else {
-                    value = "" + box.getSelectedIndex();
-                }
-            } else if (classType.equals("JSpinner")) {
-                JSpinner box = (JSpinner) obj;
-                if (write) {
-                    box.setValue(Integer.parseInt(value));
-                } else {
-                    value = "" + box.getValue();
-                }
-            } else if (classType.equals("JTabbedPane")) {
-                JTabbedPane tp = (JTabbedPane) obj;
-                if (write) {
-                    tp.setSelectedIndex(Integer.parseInt(value));
-                } else {
-                    value = "" + tp.getSelectedIndex();
-                }
-            } else if (classType.equals("ColorButton")) {
-                ColorButton tp = (ColorButton) obj;
-                if (write) {
-                    tp.setColor(value);
-                } else {
-                    value = "" + tp;
-                }
-            } else if (classType.equals("Vector")) {
-                if (name.equals("sv_lensList")) {
-                    @SuppressWarnings("unchecked")
-                    // this line raises an unavoidable warning
-                    Vector<Lens> tp = (Vector<Lens>) obj;
-                    if (write) {
-                        String s = value.replaceFirst("\\[(.*)\\]", "$1");
-                        String[] array = s.split(",");
-                        tp.removeAllElements();
-                        for (int i = 0; i < array.length; i++) {
-                            tp.add(new Lens(parent, parent.rayTraceComputer, array[i]));
-                        }
-                    } else {
-                        value = tp.toString();
-                    }
-                }
-            } else if (classType.equals("JFrame")) {
-                // only to set screen geometry
-                JFrame tp = (JFrame) obj;
-                if (obj != null) {
-                    if (write) {
-                        Rectangle r = parseRectangle(value);
-                        if (r != null) {
-                            tp.setBounds(r);
-                        }
-                    } else {
-                        value = "" + tp.getBounds();
-                    }
-                }
-            } else {
-                System.out.println(getClass().getName() + ": cannot decode value for " + classType + " (" + name + ")");
-            }
-        } catch (Exception e) {
-            System.out.println(getClass().getName() + ":readWriteField: \"" + name + "\": " + e);
-        }
-        return value;
-    }
-
-    public void writeConfig() {
-        try {
-            BufferedWriter bw = new BufferedWriter(new FileWriter(initPath));
-            bw.write(getFullConfig());
-            bw.close();
-        } catch (IOException e) {
-            System.out.println(e);
-        }
-    }
-
-    public String getFullConfig() {
-        String data = "# Source: " + parent.programName + "\n";
-        data += "# http://arachnoid.com/OpticalRayTracer\n";
-        data += "# Date: " + new Date() + "\n\n";
-        data += "# Machine-readable data:\n\n";
-        data += getConfig();
-        data += getLensDescriptions();
-        return data;
-    }
-
-    String getLensDescriptions() {
-        StringBuffer sb = new StringBuffer();
-        sb.append("\n# Human-readable lens descriptions:\n\n");
-        Iterator<Lens> it = parent.sv_lensList.iterator();
-        while (it.hasNext()) {
-            sb.append(it.next().explain() + "\n");
-        }
-        return sb.toString();
-    }
-
-    public String getConfig() {
-        Vector<String> data = new Vector<String>();
-        Hashtable<String, Field> fnht = getFieldNames();
-        //String name = "", value = "";
-        String name, value;
-        Field field;
-        Enumeration en = fnht.keys();
-        while (en.hasMoreElements()) {
-            field = fnht.get(en.nextElement());
-            name = field.getName();
-            value = readWriteField(name, null);
-            if (value != null) {
-                data.add(name + " = " + value);
-            }
-        }
-        Collections.sort(data, new StringComparatorNoCase());
-        String[] array = (String[]) data.toArray(new String[]{});
-        return join(array, lineSep) + lineSep;
-    }
-
-    Hashtable<String, Field> getFieldNames() {
-        Hashtable<String, Field> hash = new Hashtable<String, Field>();
-        Field[] fields = parent.getClass().getDeclaredFields();
-        String name;
-        for (int i = 0; i < fields.length; i++) {
-            Field field = fields[i];
-            name = field.getName();
-            if (name.matches("^" + fieldPrefix + ".*")) {
-                hash.put(name, field);
-            }
-        }
-        return hash;
-    }
-
-    String readTextFile(String path, String lineSep) {
-        String s = null;
-        File f = new File(path);
-        if (f.exists()) {
-            StringBuffer sb = new StringBuffer();
-            try {
-                BufferedReader br = new BufferedReader(new FileReader(f));
-                String line;
-                while ((line = br.readLine()) != null) {
-                    sb.append(line + lineSep);
-                }
-                br.close();
-                s = sb.toString();
-            } catch (Exception e) {
-                //System.out.println(e);
-            }
-        }
-        return s;
-    }
-
-    boolean testMakeDirs(String path) {
-        File fpath = new File(path);
-        if (fpath.exists()) {
-            return false;
-        } else {
-            fpath.mkdirs();
-            return true;
-        }
-    }
-
-    String join(String[] array, String delim) {
-        StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < array.length; i++) {
-            if (i > 0) {
-                sb.append(delim);
-            }
-            sb.append(array[i]);
-        }
-        return sb.toString();
-    }
-
-    String strip(String s) {
-        return s.replaceFirst("^\\s*(.*?)\\s*$", "$1");
-    }
-}
diff --git a/src/opticalraytracer/InitializationManager.java b/src/opticalraytracer/InitializationManager.java
new file mode 100644
index 0000000..7b96925
--- /dev/null
+++ b/src/opticalraytracer/InitializationManager.java
@@ -0,0 +1,137 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+final public class InitializationManager {
+	OpticalRayTracer parent;
+	ProgramValues programValues;
+	String fileSep;
+	String lineSep;
+	String appName;
+	String userDir;
+	String userPath;
+	String initPath;
+
+	public InitializationManager(OpticalRayTracer p, ProgramValues pv) {
+		this.parent = p;
+		this.programValues = pv;
+		lineSep = System.getProperty("line.separator");
+		fileSep = System.getProperty("file.separator");
+		appName = parent.getClass().getSimpleName();
+		userDir = System.getProperty("user.home");
+		userPath = userDir + fileSep + "." + appName;
+		initPath = userPath + fileSep + appName + ".ini";
+		testMakeDirs(userPath);
+	}
+
+	protected void setFullConfiguration(String data) {
+		if (data != null) {
+			String pv = data.replaceFirst(
+					"(?is).*?program \\{\\s*(.*?)\\s*\\}.*", "$1");
+			programValues.setValues(pv);
+			parent.componentList = new ArrayList<>();
+			String s = "(?is)object \\{\\s*(.*?)\\s*\\}";
+			Pattern pat = Pattern.compile(s);
+			Matcher m = pat.matcher(data);
+			while (m.find()) {
+				String v = m.group(1);
+				parent.makeNewComponent(v, false, Common.OBJECT_REFRACTOR);
+			}
+			parent.writeProgramControls();
+		}
+	}
+
+	protected void readConfig() {
+		String data = readFile(initPath);
+		setFullConfiguration(data);
+	}
+
+	protected void writeConfig(boolean includeHeader) {
+		writeFile(initPath, getFullConfiguration(includeHeader));
+	}
+
+	protected String getFullConfiguration(boolean includeHeader) {
+		StringBuilder sb = new StringBuilder();
+		if (includeHeader) {
+			sb.append(String.format("# %s\n", parent.fullAppName));
+			sb.append("# http://arachnoid.com/OpticalRayTracer\n");
+			String date = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z")
+					.format(new Date());
+			sb.append(String.format("# %s\n\n", date));
+		}
+		sb.append(String.format("program {\n"));
+		sb.append(programValues.getValues());
+		sb.append(String.format("}\n\n"));
+		for (OpticalComponent oc : parent.componentList) {
+			sb.append(oc.getValues());
+			sb.append("\n");
+		}
+		return sb.toString();
+	}
+
+	private String readFile(String path) {
+		String result = null;
+		try {
+			result = new String(Files.readAllBytes(Paths.get(path)),
+					StandardCharsets.UTF_8);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			// e.printStackTrace();
+			// no initialization file, so first run
+			parent.setDefaults(true);
+		}
+		return result;
+	}
+
+	private void writeFile(String path, String data) {
+		try {
+			PrintWriter out = new PrintWriter(path);
+			out.write(data);
+			out.close();
+		} catch (FileNotFoundException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	private boolean testMakeDirs(String path) {
+		File fpath = new File(path);
+		if (fpath.exists()) {
+			return false;
+		} else {
+			fpath.mkdirs();
+			return true;
+		}
+	}
+}
diff --git a/src/opticalraytracer/IntersectionSortComparator.java b/src/opticalraytracer/IntersectionSortComparator.java
index 6c55c35..ba10a86 100644
--- a/src/opticalraytracer/IntersectionSortComparator.java
+++ b/src/opticalraytracer/IntersectionSortComparator.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -29,6 +29,6 @@ import java.util.*;
 final public class IntersectionSortComparator implements Comparator<RayLensIntersection> {
 
     public int compare(RayLensIntersection s1, RayLensIntersection s2) {
-        return (s1.x < s2.x)?-1:(s1.x == s2.x)?0:1;
+        return (s1.m > s2.m)?1:(s1.m == s2.m)?0:-1;
     }
 }
diff --git a/src/opticalraytracer/Lens.java b/src/opticalraytracer/Lens.java
deleted file mode 100644
index 62748f3..0000000
--- a/src/opticalraytracer/Lens.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-import java.awt.*;
-
-/**
- *
- * @author lutusp
- */
-final public class Lens {
-
-    OpticalRayTracer parent;
-    RayTraceComputer rayTraceComputer;
-    double cx;
-    double cy;
-    double lensRadius;
-    double leftSphereRadius;
-    double rightSphereRadius;
-    double userThickness;
-    double internalThickness;
-    double ior;
-    double lcf;
-    double rcf;
-    double dispersion;
-    boolean leftHyp;
-    boolean rightHyp;
-    boolean symmetrical;
-    double leftOffset, rightOffset;
-    boolean valid = false;
-    final double delta = 1e-8;
-    final double margin = .001;
-    Box border = new Box();
-    MutableDouble dx = new MutableDouble();
-    MutableDouble dy = new MutableDouble();
-    MutableDouble xa = new MutableDouble();
-    MutableDouble xb = new MutableDouble();
-    MutableInt ix0 = new MutableInt();
-    MutableInt iy0 = new MutableInt();
-    MutableInt ix1 = new MutableInt();
-    MutableInt iy1 = new MutableInt();
-    MutableInt ix2 = new MutableInt();
-    MutableInt iy2 = new MutableInt();
-
-    public Lens(
-            OpticalRayTracer parent,
-            RayTraceComputer rtc,
-            double cx,
-            double cy,
-            double lensRadius,
-            double leftSphereRadius,
-            double rightSphereRadius,
-            double userThickness,
-            double ior,
-            double lcf,
-            double rcf,
-            double dispersion,
-            boolean leftHyp,
-            boolean rightHyp,
-            boolean symmetrical) {
-        this.parent = parent;
-        this.rayTraceComputer = rtc;
-        this.cx = cx;
-        this.cy = cy;
-        this.lensRadius = lensRadius;
-        this.leftSphereRadius = leftSphereRadius;
-        this.lcf = lcf;
-        this.rightSphereRadius = rightSphereRadius;
-        this.rcf = rcf;
-        this.userThickness = userThickness;
-        this.ior = ior;
-        this.dispersion = dispersion;
-        this.leftHyp = leftHyp;
-        this.rightHyp = rightHyp;
-        this.symmetrical = symmetrical;
-        setup();
-    }
-
-    public Lens(
-            OpticalRayTracer parent,
-            RayTraceComputer rtc,
-            String desc) {
-        this.parent = parent;
-        this.rayTraceComputer = rtc;
-        toVals(desc);
-        setup();
-    }
-    
-    void toVals(String desc) {
-        try {
-            String[] array = desc.split("\\|");
-            if (array.length == 13) {
-                int i = 0;
-                cx = parent.getDouble(array[i++]);
-                cy = parent.getDouble(array[i++]);
-                lensRadius = parent.getDouble(array[i++]);
-                leftSphereRadius = parent.getDouble(array[i++]);
-                rightSphereRadius = parent.getDouble(array[i++]);
-                userThickness = parent.getDouble(array[i++]);
-                ior = parent.getDouble(array[i++]);
-                lcf = parent.getDouble(array[i++]);
-                rcf = parent.getDouble(array[i++]);
-                dispersion = parent.getDouble(array[i++]);
-                leftHyp = array[i++].matches("true");
-                rightHyp = array[i++].matches("true");
-                symmetrical = array[i++].matches("true");
-                valid = true;
-            } else {
-                throw new Exception("malformed description string");
-            }
-        } catch (Exception e) {
-            System.out.println(getClass().getName() + ": Error: " + e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuffer ss = new StringBuffer();
-        ss.append(cx + "|");
-        ss.append(cy + "|");
-        ss.append(lensRadius + "|");
-        ss.append(leftSphereRadius + "|");
-        ss.append(rightSphereRadius + "|");
-        ss.append(userThickness + "|");
-        ss.append(ior + "|");
-        ss.append(lcf + "|");
-        ss.append(rcf + "|");
-        ss.append(dispersion + "|");
-        ss.append(leftHyp + "|");
-        ss.append(rightHyp + "|");
-        ss.append(symmetrical);
-        return ss.toString();
-    }
-
-    void setup() {
-        userThickness = (userThickness < 0)?0:userThickness;
-        ior = (ior < 1)?1:ior;
-        lensRadius = Math.abs(lensRadius);
-        if (symmetrical) {
-            rightSphereRadius = leftSphereRadius;
-            rcf = lcf;
-            rightHyp = leftHyp;
-        }
-        leftSphereRadius = checkRadius(leftSphereRadius);
-        rightSphereRadius = checkRadius(rightSphereRadius);
-        // must not aproach zero
-        lcf = (lcf < delta) ? delta : lcf;
-        rcf = (rcf < delta) ? delta : rcf;
-        leftOffset = Math.sqrt(leftSphereRadius * leftSphereRadius - lensRadius * lensRadius);
-        rightOffset = Math.sqrt(rightSphereRadius * rightSphereRadius - lensRadius * lensRadius);
-        internalThickness = 0;
-        border.left = 0;
-        border.right = 0;
-        border.top = 0;
-        border.bottom = 0;
-        dx.v = 0;
-        // pass 1: compute thickness number
-        computeBoxSize(-lensRadius, border, dx);
-        computeBoxSize(0, border, dx);
-        computeBoxSize(lensRadius, border, dx);
-        // pass 2: compute borders with thickness applied
-        border.left = 0;
-        border.right = 0;
-        dx.v += lensRadius * parent.sv_lensMinThickness;
-        dx.v += userThickness / 2.0;
-        internalThickness = dx.v;
-        computeBoxSize(-lensRadius, border, dx);
-        computeBoxSize(0, border, dx);
-        computeBoxSize(lensRadius, border, dx);
-        internalThickness = dx.v;
-        border.left -= margin;
-        border.right += margin;
-        border.top = lensRadius + margin;
-        border.bottom = -lensRadius - margin;
-    }
-
-    double checkRadius(double v) {
-        double sign = (v < 0) ? -1 : 1;
-        v = Math.abs(v);
-        v = Math.max(v, lensRadius);
-        return v * sign;
-    }
-
-    void computeBoxSize(double y, Box border, MutableDouble thick) {
-        lensXforY(y, 0, xa, xb);
-        border.left = Math.min(border.left, xa.v);
-        border.left = Math.min(border.left, xb.v);
-        border.right = Math.max(border.right, xa.v);
-        border.right = Math.max(border.right, xb.v);
-        double q = xa.v - xb.v;
-        if (q < 0) {
-            thick.v = Math.max(thick.v, -q / 2.0);
-        }
-    }
-
-    void drawBox(Graphics g, boolean focused) {
-        if (focused) {
-            double x = cx + border.left;
-            double y = cy - border.bottom;
-            double w = border.right - border.left;
-            double h = border.top - border.bottom;
-            rayTraceComputer.spaceToDisplay(x, y, ix1, iy1);
-            w = w * parent.sv_dispScale * parent.ySize;
-            h = h * parent.sv_dispScale * parent.ySize;
-            int iw = (int)w;
-            int ih = (int)h;
-            g.setColor(parent.sv_lensSelColor.getColor());
-            g.drawRect(ix1.v, iy1.v, iw,ih);
-            g.drawRect(ix1.v+1, iy1.v+1, iw-2, ih-2);
-        }
-    //parent.p(dx.v + "," + dy.v + "," + w + "," + h);
-    }
-
-    void drawLens(Graphics g) {
-        int resolution = 30;
-        g.setColor(parent.sv_lensOutlineColor.getColor());
-        for (int i = 0; i <= resolution; i++) {
-            double y = lensRadius * ((2.0 * i / (double) resolution) - 1.0);
-            lensXforY(y, cx, xa, xb);
-            rayTraceComputer.drawScaledLine(xb.v, y + cy, ix1, iy1, g, 0, (i != 0));
-            rayTraceComputer.drawScaledLine(xa.v, y + cy, ix2, iy2, g, 0, (i != 0));
-            if (i == 0 || i == resolution) {
-                rayTraceComputer.drawScaledLine(xa.v, y + cy, ix0, iy0, g, 0, false);
-                rayTraceComputer.drawScaledLine(xb.v, y + cy, ix0, iy0, g, 0, true);
-            }
-        }
-    }
-
-    boolean inside(double x, double y) {
-        return border.inside(x - cx, y - cy);
-    }
-
-    ComplexNum profile(int prof) {
-        double lo = (leftSphereRadius < 0) ? -leftOffset : leftOffset;
-        double ro = (rightSphereRadius < 0) ? -rightOffset : rightOffset;
-        double x = ((prof == 0) ? cx - lo + internalThickness : cx + ro - internalThickness);
-        return new ComplexNum(x, cy);
-    }
-
-    double tangent(int profile, double y) {
-        y -= cy;
-        double qy;
-        double rad, off;
-        rad = leftSphereRadius;
-        off = leftOffset;
-        double cf = lcf;
-        if (profile == 1) {
-            rad = -rightSphereRadius;
-            off = rightOffset;
-            cf = rcf;
-        }
-        if ((profile == 0 && leftHyp) || (profile == 1 && rightHyp)) {
-            qy = rayTraceComputer.hyperbolicLensProfileDYforY(y, cx, cf, rad, off, -internalThickness);
-        } else {
-            qy = rayTraceComputer.sphericalLensProfileDYforY(y, cx, rad, off, -internalThickness);
-        }
-        return qy;
-    }
-
-    void computeIntersections(int profile, double x1, double y1, double x2, double y2, double ccx, double ccy, MutableDouble xa, MutableDouble ya, MutableDouble xb, MutableDouble yb) {
-        double srad = (profile == 0) ? leftSphereRadius : rightSphereRadius;
-        double sr = (srad < 0) ? -srad : srad;
-        double offset = (profile == 0) ? leftOffset : rightOffset;
-        double r = 0, off = 0, d = 0, th = 0;
-        if ((profile == 0 && leftHyp) || (profile == 1 && rightHyp)) {
-            r = Math.sqrt(sr * sr - offset * offset);
-            off = offset;
-            d = sr - off;
-            th = internalThickness;
-            double cf = rcf;
-            if (profile == 0) {
-                d = -d;
-                th = -th;
-                cf = lcf;
-            }
-            if (srad < 0) {
-                d = -d;
-            }
-            rayTraceComputer.hyp_line_intersections(x1, y1, x2, y2, cf, r, d, cx - th, cy, xa, ya, xb, yb);
-        } else {
-            rayTraceComputer.circle_line_intersections(x1, y1, x2, y2, sr, ccx, ccy, xa, ya, xb, yb);
-        }
-    }
-
-    void lensXforY(double y, double ccx, MutableDouble xa, MutableDouble xb) {
-        if (leftHyp) {
-            xa.v = rayTraceComputer.hyperbolicLensProfileXforY(y, ccx, lcf, -leftSphereRadius, leftOffset, internalThickness);
-        } else {
-            xa.v = rayTraceComputer.sphericalLensProfileXforY(y, ccx, -leftSphereRadius, leftOffset, internalThickness);
-        }
-        // right
-        if (rightHyp) {
-            xb.v = rayTraceComputer.hyperbolicLensProfileXforY(y, ccx, rcf, rightSphereRadius, rightOffset, -internalThickness);
-        } else {
-            xb.v = rayTraceComputer.sphericalLensProfileXforY(y, ccx, rightSphereRadius, rightOffset, -internalThickness);
-        }
-    }
-
-    public String explain() {
-        StringBuffer ss = new StringBuffer();
-        ss.append("# X position = " + cx + "\n");
-        ss.append("# Y position = " + cy + "\n");
-        ss.append("# Lens Radius = " + lensRadius + "\n");
-        ss.append("# Left Sphere Radius = " + leftSphereRadius + "\n");
-        ss.append("# Right Sphere Radius = " + rightSphereRadius + "\n");
-        ss.append("# Lens Thickness = " + userThickness + "\n");
-        ss.append("# Index of Refraction = " + ior + "\n");
-        ss.append("# Left Curvature Factor = " + lcf + "\n");
-        ss.append("# Right Curvature Factor = " + rcf + "\n");
-        ss.append("# Dispersion (abbe) = " + dispersion + "\n");
-        ss.append("# Left Hyperboloid = " + leftHyp + "\n");
-        ss.append("# Right Hyperboloid = " + rightHyp + "\n");
-        ss.append("# Symmetrical = " + symmetrical + "\n");
-        return ss.toString();
-    }
-}
diff --git a/src/opticalraytracer/LineAnalysis.java b/src/opticalraytracer/LineAnalysis.java
new file mode 100644
index 0000000..b78848a
--- /dev/null
+++ b/src/opticalraytracer/LineAnalysis.java
@@ -0,0 +1,270 @@
+package opticalraytracer;
+
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JTextArea;
+
+final public class LineAnalysis {
+	OpticalRayTracer parent;
+	LineData closestLine;
+	double minValue;
+	String[] header = new String[] { "From","To", "Source", "Destination",
+			"DestinationType", "FromX", "FromY", "ToX", "ToY", "DeltaX",
+			"DeltaY", "Magnitude", "BeamAngle", "SurfaceNormalAngle",
+			"WavelengthNM" };
+
+	String[] css = {"<style type=\"text/css\">",
+			"   body {",
+			"     font-family:monospace;",
+			"   }",
+			"   table {",
+			"     border-collapse:collapse;",
+			"   }",
+			"   table * {",
+			"     border:1px solid gray;",
+			"     white-space:nowrap;",
+			"   }",
+			"   td {",
+			"     text-align:right;",
+			"   }",
+			"   td.lj {",
+			"     text-align:left;",
+			"   }",
+			"   tr:nth-child(even) {",
+			"     background: #ffffff;",
+			"   }",
+			"   tr:nth-child(odd) {",
+			"     background: #f0f0f0;",
+			"   }",
+			"   th {",
+			"     text-align:center;",
+			"     font-weight:bold;",
+			"     background:#c0d0c0;",
+			"   }",
+			"  </style>" };
+
+	String meta = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n";
+
+	public LineAnalysis(OpticalRayTracer p) {
+		parent = p;
+	}
+
+	protected String fmtNum(double v) {
+		return parent.formatNum(v);
+	}
+
+	protected String makeRow(LineData ld, String token) {
+		StringBuilder sb = new StringBuilder();
+		ArrayList<String> array = makeRow(ld);
+		boolean start = true;
+		for(String s : array) {
+			if(!start) {
+				sb.append(token);
+			}
+			start = false;
+			sb.append(s);
+		}
+		return sb.toString();
+	}
+	
+	protected ArrayList<String> makeRow(LineData ld) {
+		ArrayList<String> array = new ArrayList<>();
+		array.add(ld.fromEvent);
+		array.add(ld.toEvent);
+		array.add(ld.from);
+		array.add(ld.to);
+		array.add(ld.type);
+		for (double v : ld.numericValues()) {
+			array.add(parent.formatNum(v));
+		}
+		return array;
+	}
+	
+	protected boolean isRightJust(int n) {
+		boolean rj = true;
+		if(parent.dataTableDisplay.dataTableModel != null) {
+			boolean[] rightJust = parent.dataTableDisplay.dataTableModel.rightJust;
+			if(n >= 0 && n < rightJust.length) {
+				rj = rightJust[n];
+			}
+		}
+		return rj;
+	}
+
+	protected String createHTMLTable(boolean linefeeds) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("<tr><th>");
+		sb.append(makeHeader("</th><th>"));
+		sb.append("</th></tr>");
+		if (linefeeds) {
+			sb.append("\n");
+		}
+		for (LineData ld : parent.rayTraceComputer.lineList) {
+			sb.append("<tr>");
+			ArrayList<String> sa = makeRow(ld);
+			int cn = 0;
+			for(String s : sa) {
+				boolean rj = isRightJust(cn);
+				String cls = (rj)?"":"class = \"lj\"";
+				String ws = Common.wrapTag("td",s,cls,false);
+				sb.append(ws);
+				cn += 1;
+			}
+			sb.append("</tr>");
+			if (linefeeds) {
+				sb.append("\n");
+			}
+			
+		}
+		String result = "";
+		result = Common.wrapTag("table", sb.toString(),
+				"cellspacing=\"0\" cellpadding=\"2\"", linefeeds);
+		result = Common.wrapTag("body", result, "", linefeeds);
+		sb = new StringBuilder();
+		sb.append("<head>\n");
+		sb.append(meta);
+		for (String s : css) {
+			sb.append(s);
+			sb.append("\n");
+		}
+		sb.append("</head>\n");
+		sb.append(result);
+		result = sb.toString();
+		result = Common.wrapTag("html", result, "", linefeeds);
+		return result;
+	}
+
+	protected String makeHTMLTable(boolean linefeeds) {
+		parent.rayTraceComputer.traceRays(null, true);
+		return createHTMLTable(linefeeds);
+	}
+
+	protected String makeCSVTable() {
+		parent.rayTraceComputer.traceRays(null, true);
+		StringBuilder sb = new StringBuilder();
+		sb.append(makeHeader("\t"));
+		sb.append("\n");
+		for (LineData ld : parent.rayTraceComputer.lineList) {
+			sb.append(makeRow(ld,"\t"));
+			sb.append("\n");
+		}
+		return sb.toString();
+	}
+
+	protected String makeHeader(String token) {
+		StringBuilder sb = new StringBuilder();
+		int n = 0;
+		int len = header.length;
+		for (String s : header) {
+			sb.append(s);
+			n += 1;
+			if (n < len) {
+				sb.append(token);
+			}
+		}
+		return sb.toString();
+	}
+
+	protected void nearestLineProperties(double mx, double my) {
+		//Common.p("nearestlineproperties");
+		closestLine = null;
+		parent.rayTraceComputer.traceRays(null, true);
+		minValue = 1e9;
+		for (LineData ld : parent.rayTraceComputer.lineList) {
+			compare(ld, mx, my);
+		}
+		if (closestLine != null) {
+			StringBuilder sb = new StringBuilder();
+			sb.append(String.format("%-24s: %13s%13s\n", "Property", "x", "y"));
+			sb.append("----------------------------------------------------\n");
+			sb.append(String.format("%-24s: {%12s,%12s}\n", "Origin",
+					fmtNum(closestLine.a.x), fmtNum(closestLine.a.y)));
+			sb.append(String.format("%-24s: {%12s,%12s}\n", "Destination",
+					fmtNum(closestLine.b.x), fmtNum(closestLine.b.y)));
+			sb.append(String.format("%-24s: {%12s,%12s}\n", "Line length",
+					fmtNum(closestLine.b.x - closestLine.a.x),
+					fmtNum(closestLine.b.y - closestLine.a.y)));
+			sb.append(String.format("%-24s: %26s\n", "Magnitude",
+					fmtNum(closestLine.m)));
+			sb.append(String.format("%-24s: %26s°\n", "Beam Angle",
+					fmtNum(closestLine.ar)));
+			sb.append(String.format("%-24s: %26s°\n", "Dest. Surface Normal",
+					fmtNum(closestLine.sa)));
+			sb.append(String.format("%-24s: %26s\n", "Wavelength NM",
+					fmtNum(closestLine.wavelength)));
+			
+			sb.append(String
+					.format("%-24s: %26s\n", "From", closestLine.fromEvent));
+			sb.append(String
+					.format("%-24s: %26s\n", "To", closestLine.toEvent));
+			sb.append(String.format("%-24s: %26s\n", "Source",
+					closestLine.from));
+			sb.append(String.format("%-24s: %26s\n", "Destination", closestLine.to));
+			sb.append(String.format("%-24s: %26s\n", "Destination Type",
+					closestLine.type));
+			showInfoDialog(sb.toString(), "Line Properties");
+		} else {
+			parent.showNotifyMessage("No Nearby Line", "Line Properties");
+		}
+	}
+	
+	protected void showInfoDialog(String message, String title) {
+		JTextArea ta = new JTextArea(message);
+		JButton copyButton = new JButton("Copy to clipboard");
+		copyButton.setToolTipText("Copy this line to the system clipboard as a tab-separated record");
+		copyButton.addActionListener(
+				new ActionListener() {
+
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						// TODO Auto-generated method stub
+					copyNearestLineToClipboard();	
+					}
+				}
+				);
+		Object content = new Object[] {ta,copyButton};
+		ta.setBackground(parent.frame.getBackground());
+		Font f = new Font("Monospaced", Font.PLAIN, 11);
+		ta.setFont(f);
+		JOptionPane.showMessageDialog(parent.frame, content, parent.appName + ": " + title,
+				JOptionPane.INFORMATION_MESSAGE);
+	}
+	
+	protected void copyLineToClipboard(LineData ld) {
+		if(ld != null) {
+			StringBuilder sb = new StringBuilder();
+			sb.append(makeHeader("\t"));
+			sb.append("\n");
+			sb.append(makeRow(ld,"\t"));
+			sb.append("\n");
+			parent.clipboardCopyString(sb.toString());
+		}
+		else {
+			Common.beep();
+		}
+	}
+	
+	protected void copyNearestLineToClipboard() {
+		copyLineToClipboard(closestLine);
+	}
+
+	protected void compare(LineData ld, double mx, double my) {
+		double lx = Common.xCoordinateOnLine(mx, my, ld.a.x, ld.a.y, ld.b.x, ld.b.y);
+		double ly = Common.yCoordinateOnLine(mx, my, ld.a.x, ld.a.y, ld.b.x, ld.b.y);
+		double m = Common.distanceToLine(mx, my, ld.a.x, ld.a.y, ld.b.x, ld.b.y);
+		if (Common.inBounds(lx, ly, ld.a.x, ld.a.y, ld.b.x, ld.b.y)) {
+			// update closest line
+			if (minValue > m) {
+				closestLine = ld;
+				minValue = m;
+			}
+		}
+	}
+
+	
+}
diff --git a/src/opticalraytracer/LineData.java b/src/opticalraytracer/LineData.java
new file mode 100644
index 0000000..ffd0a94
--- /dev/null
+++ b/src/opticalraytracer/LineData.java
@@ -0,0 +1,47 @@
+package opticalraytracer;
+
+import static java.lang.Math.*;
+
+final public class LineData {
+	String from = "", to = "", type = "",fromEvent = "",toEvent = "";
+	Vector a, b;
+	double dx, dy;
+	double m, ar, sa;
+	double wavelength;
+
+	public LineData(int ray, int dbeam, double wavelength,
+			RayLensIntersection oldrli, RayLensIntersection rli, Vector ia,
+			Vector ib, double surfaceAngle, String fromEvent, String toEvent) {
+		this.fromEvent = fromEvent;
+		this.toEvent = toEvent;
+		if (oldrli == null) {
+			from = String.format("Origin Ray %d", ray + 1);
+			if (wavelength != 0) {
+				from += String.format(" Dbeam %d", dbeam + 1);
+			}
+		} else {
+			from = oldrli.lens.values.name;
+		}
+		if (rli == null) {
+			this.to = "Virtual space boundary";
+			type = "Domain Limit"; 
+		} else {
+			this.to = rli.lens.values.name;
+			type = Common.getObjectType(rli.function);
+		}
+		// must deep copy
+		this.a = new Vector(ia.x, ia.y);
+		this.b = new Vector(ib.x, ib.y);
+		dx = ib.x - ia.x;
+		dy = ib.y - ia.y;
+		m = sqrt(dx * dx + dy * dy);
+		this.wavelength = (wavelength == 0)?WavelengthColor.dispersionPivotNM:wavelength;
+		ar = atan2(dy, dx) * Common.degrees;
+		sa = surfaceAngle * Common.degrees;
+		
+	}
+
+	public double[] numericValues() {
+		return new double[] { a.x, a.y, b.x, b.y, dx, dy, m, ar, sa, wavelength };
+	}
+}
diff --git a/src/opticalraytracer/LocaleHandler.java b/src/opticalraytracer/LocaleHandler.java
new file mode 100644
index 0000000..9073354
--- /dev/null
+++ b/src/opticalraytracer/LocaleHandler.java
@@ -0,0 +1,103 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.abs;
+import static java.lang.Math.pow;
+
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+final public class LocaleHandler {
+	
+	static final String localeDecimalSeparator = "" + new DecimalFormatSymbols(Locale.getDefault()).getDecimalSeparator();
+
+	static double getDouble(String s, String decSep) {
+		double v = 0;
+		double pow = 0;
+		String exp = "";
+		try {
+			if (s != null) {
+				s = s.replaceAll("[.,]", decSep);
+				Locale l = Locale.getDefault();
+				NumberFormat nf = NumberFormat.getInstance(l);
+				s = s.toLowerCase();
+				s = s.trim();
+				String[] array = s.split("e");
+				if (array.length > 1) {
+					// get signed exponent
+					exp = array[1].trim().replaceAll("\\+", "");
+					if (exp.length() > 0) {
+						pow = Double.parseDouble(exp);
+					}
+				}
+				v = nf.parse(array[0]).doubleValue();
+				v = v * pow(10, pow);
+			}
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return v;
+	}
+
+	static int getInt(String s) {
+		int v = 0;
+		Locale l = Locale.getDefault();
+		NumberFormat nf = NumberFormat.getInstance(l);
+		try {
+			v = nf.parse(s).intValue();
+		} catch (ParseException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return v;
+	}
+
+	static String formatDouble(double v, int decimals) {
+		Locale l = Locale.getDefault();
+		String result = "";
+		if (abs(v) >= 1e-3 && abs(v) < 1e7) {
+			result = String.format(l, "%." + decimals + "f", v);
+		} else {
+			result = String.format(l, "%." + decimals + "e", v);
+		}
+		return result;
+	}
+
+	static String formatDouble(double v) {
+		Locale l = Locale.getDefault();
+		String result = "";
+		if (abs(v) >= 1e-3) {
+			result = String.format(l, "%f", v);
+		} else {
+			result = String.format(l, "%e", v);
+		}
+		return result;
+	}
+
+	static String formatInt(int v) {
+		Locale l = Locale.getDefault();
+		return String.format(l, "%d", v);
+	}
+}
diff --git a/src/opticalraytracer/MutableDouble.java b/src/opticalraytracer/MutableDouble.java
deleted file mode 100644
index 82756dc..0000000
--- a/src/opticalraytracer/MutableDouble.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-/**
- *
- * @author lutusp
- */
-public class MutableDouble {
-
-    double v;
-
-    public MutableDouble(double v) {
-        this.v = v;
-    }
-    public MutableDouble() {
-    }
-}
diff --git a/src/opticalraytracer/MyColor.java b/src/opticalraytracer/MyColor.java
index 1cc7e1c..671e031 100644
--- a/src/opticalraytracer/MyColor.java
+++ b/src/opticalraytracer/MyColor.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,29 +17,41 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
+
 package opticalraytracer;
 
 import java.awt.*;
 
-/**
- *
- * @author lutusp
- */
+ at SuppressWarnings("serial")
 final public class MyColor extends Color {
 
-    public MyColor(int col) {
-        super(col);
-    }
+	// use alpha if present but set alpha = 0xff if not
+	public MyColor(int rgb) {
+		super((rgb >> 16) & 0xff, (rgb >> 8) & 0xff,rgb & 0xff,(((rgb >> 24 & 0xff) == 0)?0xff:rgb >> 24 & 0xff));
+	}
+
+	public MyColor(double r, double g, double b, double a) {
+		super((int) (r * 255), (int) (g * 255), (int) (b * 255),
+				(int) (a * 255));
+	}
+
+	public MyColor(double r, double g, double b) {
+		super((int) (r * 255), (int) (g * 255), (int) (b * 255));
+	}
+
+	public MyColor(int r, int g, int b) {
+		super(r, g, b);
+	}
 
-    public MyColor(double r, double g, double b) {
-        super((int) (r * 255), (int) (g * 255), (int) (b * 255));
-    }
+	public MyColor(int r, int g, int b, int a) {
+		super(r, g, b, a);
+	}
 
-    public MyColor(int r, int g, int b) {
-        super(r, g, b);
-    }
+	public MyColor(final int rgb, int alpha) {
+		super((rgb >> 16) & 0xff, (rgb >> 8) & 0xff,rgb & 0xff, alpha);
+	}
+	
+	public String toString() {
+		return String.format("{%d,%d,%d}" , getRed(),getGreen(),getBlue());
+	}
 }
diff --git a/src/opticalraytracer/MyHelpPane.form b/src/opticalraytracer/MyHelpPane.form
deleted file mode 100644
index 9c9c77c..0000000
--- a/src/opticalraytracer/MyHelpPane.form
+++ /dev/null
@@ -1,109 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" ?>
-
-<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
-  </AuxValues>
-
-  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-  <SubComponents>
-    <Container class="javax.swing.JPanel" name="jPanel1">
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
-        </Constraint>
-      </Constraints>
-
-      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
-      <SubComponents>
-        <Component class="javax.swing.JButton" name="undoButton">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/edit-undo.png"/>
-            </Property>
-            <Property name="toolTipText" type="java.lang.String" value="Go back"/>
-            <Property name="focusable" type="boolean" value="false"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="undoButtonMouseClicked"/>
-          </Events>
-        </Component>
-        <Component class="javax.swing.JButton" name="redoButton">
-          <Properties>
-            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-              <Image iconType="3" name="/opticalraytracer/icons/edit-redo.png"/>
-            </Property>
-            <Property name="toolTipText" type="java.lang.String" value="Go forward"/>
-            <Property name="focusable" type="boolean" value="false"/>
-          </Properties>
-          <Events>
-            <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="redoButtonMouseClicked"/>
-          </Events>
-        </Component>
-        <Component class="javax.swing.JLabel" name="jLabel1">
-          <Properties>
-            <Property name="text" type="java.lang.String" value="Search:"/>
-          </Properties>
-        </Component>
-        <Component class="javax.swing.JTextField" name="sv_findTextField">
-          <Properties>
-            <Property name="toolTipText" type="java.lang.String" value="<html>Quick search: type a search string,<br/>press Enter to find the next case</html>"/>
-            <Property name="focusCycleRoot" type="boolean" value="true"/>
-            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-              <Dimension value="[150, 27]"/>
-            </Property>
-            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-              <Dimension value="[150, 27]"/>
-            </Property>
-          </Properties>
-          <Events>
-            <EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="sv_findTextFieldKeyReleased"/>
-          </Events>
-          <AuxValues>
-            <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-          </AuxValues>
-        </Component>
-      </SubComponents>
-    </Container>
-    <Container class="javax.swing.JScrollPane" name="helpScrollPane">
-      <Properties>
-        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-          <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
-            <LineBorder/>
-          </Border>
-        </Property>
-      </Properties>
-      <AuxValues>
-        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
-      </AuxValues>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="1.0" weightY="1.0"/>
-        </Constraint>
-      </Constraints>
-
-      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
-      <SubComponents>
-        <Component class="javax.swing.JTextPane" name="helpTextPane">
-          <Properties>
-            <Property name="contentType" type="java.lang.String" value="text/html"/>
-            <Property name="editable" type="boolean" value="false"/>
-            <Property name="focusable" type="boolean" value="false"/>
-          </Properties>
-          <Events>
-            <EventHandler event="hyperlinkUpdate" listener="javax.swing.event.HyperlinkListener" parameters="javax.swing.event.HyperlinkEvent" handler="helpTextPaneHyperlinkUpdate"/>
-          </Events>
-        </Component>
-      </SubComponents>
-    </Container>
-  </SubComponents>
-</Form>
diff --git a/src/opticalraytracer/MyHelpPane.java b/src/opticalraytracer/MyHelpPane.java
index 81f2448..c6803ce 100644
--- a/src/opticalraytracer/MyHelpPane.java
+++ b/src/opticalraytracer/MyHelpPane.java
@@ -1,359 +1,399 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-/*
- * MyHelpPane.java
- *
- * Created on Feb 18, 2009, 2:12:32 PM
- */
 package opticalraytracer;
 
-import javax.swing.*;
-import java.net.*;
-import javax.swing.event.*;
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.text.*;
-import javax.swing.text.html.*;
-import javax.swing.text.html.HTMLDocument.*;
-import java.util.*;
-import java.io.*;
-
-/**
- *
- * @author lutusp
- */
-final public class MyHelpPane extends javax.swing.JPanel {
-
-    OpticalRayTracer parent;
-    Stack<Integer> undoStack;
-    Stack<Integer> redoStack;
-    Document doc;
-    String oldSearch = "";
-    int oldPos = 0;
-    Object oldHighlight = null;
-    Highlighter highlighter;
-    Highlighter.HighlightPainter highlightPainter;
-
-    /** Creates new form MyHelpPane */
-    public MyHelpPane(OpticalRayTracer p) {
-        parent = p;
-        initComponents();
-        doc = helpTextPane.getDocument();
-        undoStack = new Stack<Integer>();
-        redoStack = new Stack<Integer>();
-        highlighter = helpTextPane.getHighlighter();
-        highlightPainter = new DefaultHighlighter.DefaultHighlightPainter(new Color(200, 255, 200));
-        setupHelp();
-        setButtons();
-        SwingUtilities.invokeLater(new Runnable() {
-
-            public void run() {
-                helpScrollPane.getVerticalScrollBar().setValue(parent.sv_helpScrollPos);
-            }
-        });
-    }
-
-    public int getScrollPos() {
-        return helpScrollPane.getVerticalScrollBar().getValue();
-    }
-
-    public void setScrollPos(int v) {
-        helpScrollPane.getVerticalScrollBar().setValue(v);
-    }
-
-    // help resource related
-    void setupHelp() {
-        String fn = "help_resources/HelpText.html";
-        try {
-            InputStream is = parent.getClass().getResourceAsStream(fn);
-            InputStreamReader isr = new InputStreamReader(is);
-            BufferedReader br = new BufferedReader(isr);
-            StringBuilder sb = new StringBuilder();
-            String line;
-            while ((line = br.readLine()) != null) {
-                sb.append(line + "\n");
-            }
-            is.close();
-            String s = sb.toString();
-            s = s.replaceAll("#version#", parent.appVersion);
-            String ud = parent.initManager.userPath.replaceAll("\\\\","\\\\\\\\");
-            s = s.replaceAll("#userdir#", ud);
-            helpTextPane.setText(s);
-            helpTextPane.select(0, 0);
-            URL url = parent.getClass().getResource(fn).toURI().toURL();
-            ((HTMLDocument) helpTextPane.getDocument()).setBase(url);
-        } catch (Exception e) {
-            System.out.println("setupHelp: " + e);
-            e.printStackTrace();
-        }
-    }
-
-    // manageHyperlinks tries to find and launch a browser
-    void manageHyperlinks(HyperlinkEvent evt) {
-        URL url = evt.getURL();
-        if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
-            String surl = evt.getURL().toString();
-            if (surl.matches("http://.*")) {
-                //if (parent.applet) {
-                //    parent.setStatus("Applets can't follow hyperlinks");
-                //} else {
-                    try {
-                        java.awt.Desktop.getDesktop().browse(java.net.URI.create(surl));
-                    } catch (Exception e) {
-                        System.out.println(e);
-                    }
-                //}
-            } else if (surl.matches(".*#.*")) { // bookmark?
-                try {
-                    pushUndo();
-                    helpTextPane.scrollToReference(url.getRef());
-
-                } catch (Exception e) {
-                    System.out.println(e);
-                }
-            } else if (surl.matches(".*file:.*")) {
-                String lensSpec = surl.replaceFirst("(?ms).*file:.*'(.*?)'.*", "$1");
-                parent.setClipboardContents(lensSpec);
-            }
-        }
-    }
-
-    void undo() {
-        if (undoStack.size() > 0) {
-            removeOldHighlight();
-            pushRedo();
-
-            helpScrollPane.getVerticalScrollBar().setValue(undoStack.pop());
-            setButtons();
-
-        } else {
-            parent.beep();
-        }
-
-    }
-
-    void redo() {
-        if (redoStack.size() > 0) {
-            removeOldHighlight();
-            pushUndo();
-
-            helpScrollPane.getVerticalScrollBar().setValue(redoStack.pop());
-            setButtons();
-
-        } else {
-            parent.beep();
-        }
-
-    }
-
-    void pushUndo() {
-        undoStack.push(helpScrollPane.getVerticalScrollBar().getValue());
-        setButtons();
-
-    }
-
-    void pushRedo() {
-        redoStack.push(helpScrollPane.getVerticalScrollBar().getValue());
-        setButtons();
-
-    }
-
-    void setButtons() {
-        undoButton.setEnabled(undoStack.size() > 0);
-        redoButton.setEnabled(redoStack.size() > 0);
-    }
-
-    void removeOldHighlight() {
-        if (oldHighlight != null) {
-            highlighter.removeHighlight(oldHighlight);
-            oldHighlight =
-                    null;
-        }
-
-    }
-
-    void manageHelpTextField(KeyEvent evt) {
-        //String code = KeyEvent.getKey_Text(evt.getKeyCode());
-        // if a function key, go to main command switchboard
-        //if (code.matches("F\\d")) {
-        //parent.handleKeyPressed(evt);
-        //} else {
-        try {
-
-            removeOldHighlight();
-            doc =
-                    helpTextPane.getDocument();
-            int len = doc.getLength();
-            String content = doc.getText(0, len).toLowerCase();
-            String search = sv_findTextField.getText().toLowerCase();
-            if (!search.equals(oldSearch)) {
-                oldPos = 0;
-            }
-
-            oldSearch = search;
-            int p = content.indexOf(search, oldPos);
-            if (p == -1) {
-                oldPos = 0;
-                p =
-                        content.indexOf(search, oldPos);
-            }
-
-            if (p >= 0) { // if found
-                pushUndo();
-                int slen = search.length();
-                Rectangle r = helpTextPane.modelToView(p);
-                // aim for the middle of the screen
-                int pos = r.y - helpScrollPane.getHeight() / 2;
-                // but don't try for the impossible
-                pos =
-                        (pos < 0) ? 0 : pos;
-                helpScrollPane.getVerticalScrollBar().setValue(pos);
-                // now highlight the found text in our nonfocused text pane
-                oldHighlight =
-                        highlighter.addHighlight(p, p + slen, highlightPainter);
-                oldPos =
-                        p + 1; // to find next case
-            } else {
-                parent.beep();
-            }
-
-        } catch (Exception e) {
-            System.out.println(e);
-        }
-//}
-
-    }
-
-    /** This method is called from within the constructor to
-     * initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is
-     * always regenerated by the Form Editor.
-     */
-    @SuppressWarnings("unchecked")
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-        java.awt.GridBagConstraints gridBagConstraints;
-
-        jPanel1 = new javax.swing.JPanel();
-        undoButton = new javax.swing.JButton();
-        redoButton = new javax.swing.JButton();
-        jLabel1 = new javax.swing.JLabel();
-        sv_findTextField = new javax.swing.JTextField();
-        helpScrollPane = new javax.swing.JScrollPane();
-        helpTextPane = new javax.swing.JTextPane();
-
-        setLayout(new java.awt.GridBagLayout());
-
-        undoButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-undo.png"))); // NOI18N
-        undoButton.setToolTipText("Go back");
-        undoButton.setFocusable(false);
-        undoButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                undoButtonMouseClicked(evt);
-            }
-        });
-        jPanel1.add(undoButton);
-
-        redoButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-redo.png"))); // NOI18N
-        redoButton.setToolTipText("Go forward");
-        redoButton.setFocusable(false);
-        redoButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                redoButtonMouseClicked(evt);
-            }
-        });
-        jPanel1.add(redoButton);
-
-        jLabel1.setText("Search:");
-        jPanel1.add(jLabel1);
-
-        sv_findTextField.setToolTipText("<html>Quick search: type a search string,<br/>press Enter to find the next case</html>");
-        sv_findTextField.setFocusCycleRoot(true);
-        sv_findTextField.setMinimumSize(new java.awt.Dimension(150, 27));
-        sv_findTextField.setPreferredSize(new java.awt.Dimension(150, 27));
-        sv_findTextField.addKeyListener(new java.awt.event.KeyAdapter() {
-            public void keyReleased(java.awt.event.KeyEvent evt) {
-                sv_findTextFieldKeyReleased(evt);
-            }
-        });
-        jPanel1.add(sv_findTextField);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        add(jPanel1, gridBagConstraints);
-
-        helpScrollPane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-
-        helpTextPane.setBackground(java.awt.Color.white);
-        helpTextPane.setContentType("text/html");
-        helpTextPane.setEditable(false);
-        helpTextPane.setFocusable(false);
-        helpTextPane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
-            public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
-                helpTextPaneHyperlinkUpdate(evt);
-            }
-        });
-        helpScrollPane.setViewportView(helpTextPane);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.weighty = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
-        add(helpScrollPane, gridBagConstraints);
-    }// </editor-fold>//GEN-END:initComponents
-
-    private void helpTextPaneHyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {//GEN-FIRST:event_helpTextPaneHyperlinkUpdate
-        // TODO add your handling code here:
-        manageHyperlinks(evt);
-    }//GEN-LAST:event_helpTextPaneHyperlinkUpdate
-
-    private void undoButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_undoButtonMouseClicked
-        // TODO add your handling code here:
-        undo();
-}//GEN-LAST:event_undoButtonMouseClicked
-
-    private void redoButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_redoButtonMouseClicked
-        // TODO add your handling code here:
-        redo();
-}//GEN-LAST:event_redoButtonMouseClicked
-
-    private void sv_findTextFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_sv_findTextFieldKeyReleased
-        // TODO add your handling code here:
-        manageHelpTextField(evt);
-}//GEN-LAST:event_sv_findTextFieldKeyReleased
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JScrollPane helpScrollPane;
-    private javax.swing.JTextPane helpTextPane;
-    private javax.swing.JLabel jLabel1;
-    private javax.swing.JPanel jPanel1;
-    private javax.swing.JButton redoButton;
-    protected javax.swing.JTextField sv_findTextField;
-    private javax.swing.JButton undoButton;
-    // End of variables declaration//GEN-END:variables
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Stack;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.JTextPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Document;
+import javax.swing.text.Highlighter;
+import javax.swing.text.Highlighter.Highlight;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+import net.miginfocom.swing.MigLayout;
+import javax.swing.SwingConstants;
+
+ at SuppressWarnings("serial")
+final public class MyHelpPane extends JPanel {
+	int invocationCount = 0;
+	protected JTextField findTextField;
+	JScrollPane helpScrollPane;
+	OpticalRayTracer parent;
+	Stack<HelpState> undoStack;
+	Stack<HelpState> redoStack;
+	Document doc;
+	String oldSearch = "";
+	int oldPos = 0;
+	Object oldHighlight = null;
+	Highlighter highlighter;
+	Highlighter.HighlightPainter highlightPainter;
+	JTextPane helpTextPane;
+	JButton undoButton, redoButton;
+	JButton launchButton;
+	JFrame separateFrame;
+	MyHelpPane childHelp = null;
+
+	/**
+	 * Create the panel.
+	 */
+	public MyHelpPane(OpticalRayTracer p, int count) {
+		invocationCount = count + 1;
+		parent = p;
+		setLayout(new BorderLayout(0, 0));
+		setFocusable(true);
+		setRequestFocusEnabled(true);
+		helpTextPane = new JTextPane();
+		helpTextPane.setFocusable(false);
+		helpTextPane.setBackground(java.awt.Color.white);
+		helpTextPane.setContentType("text/html;charset=UTF-8");
+		helpTextPane.setEditorKit(new HTMLEditorKit());
+		helpTextPane.setEditable(false);
+		// helpTextPane.setFont(new Font("monospaced",Font.PLAIN,16));
+		// try DejaVu Sans?
+		// helpTextPane.setFocusable(false);
+		helpTextPane
+				.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
+					public void hyperlinkUpdate(
+							javax.swing.event.HyperlinkEvent evt) {
+						manageHyperlinks(evt);
+					}
+				});
+		doc = helpTextPane.getDocument();
+		undoStack = new Stack<>();
+		redoStack = new Stack<>();
+		highlighter = helpTextPane.getHighlighter();
+		highlightPainter = new DefaultHighlighter.DefaultHighlightPainter(
+				new Color(200, 255, 200));
+
+		helpScrollPane = new JScrollPane();
+		add(helpScrollPane, BorderLayout.CENTER);
+
+		helpScrollPane.setViewportView(helpTextPane);
+
+		JPanel controlPanel = new JPanel();
+		add(controlPanel, BorderLayout.SOUTH);
+
+		launchButton = new JButton("");
+		launchButton.setIcon(new ImageIcon(MyHelpPane.class
+				.getResource("/opticalraytracer/icons/system-help.png")));
+		launchButton.setToolTipText("Launch copy of help in separate window");
+		launchButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				launchHelpInFrame();
+			}
+		});
+		controlPanel.setLayout(new MigLayout("", "[]push[][][][]push"));
+		controlPanel.add(launchButton, "cell 0 0,alignx left");
+
+		findTextField = new JTextField();
+		findTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		findTextField
+				.setToolTipText("<html>Quick search: type a search string,<br/>press Enter to find the next case</html>");
+		findTextField.addKeyListener(new KeyAdapter() {
+			@Override
+			public void keyReleased(KeyEvent e) {
+				manageHelpTextField(e);
+			}
+		});
+
+		redoButton = new JButton("");
+		redoButton.setToolTipText("Redo previously undone action");
+		redoButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				redoPop();
+			}
+		});
+
+		undoButton = new JButton("");
+		undoButton.setToolTipText("Undo prior action");
+		undoButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				undoPop();
+			}
+		});
+		undoButton.setIcon(new ImageIcon(MyHelpPane.class
+				.getResource("/opticalraytracer/icons/edit-undo.png")));
+		controlPanel.add(undoButton, "cell 2 0,alignx right");
+		redoButton.setIcon(new ImageIcon(MyHelpPane.class
+				.getResource("/opticalraytracer/icons/edit-redo.png")));
+		controlPanel.add(redoButton, "cell 3 0,alignx right");
+		controlPanel.add(findTextField, "cell 4 0,alignx right");
+		findTextField.setColumns(10);
+		setupHelp();
+		setButtons();
+		addFocusListener(new FocusAdapter() {
+			@Override
+			public void focusGained(FocusEvent e) {
+				findTextField.requestFocus();
+				findTextField.requestFocusInWindow();
+			}
+		});
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				onStart();
+			}
+		});
+	}
+
+	protected void onStart() {
+		helpScrollPane.getVerticalScrollBar().setValue(
+				parent.programValues.helpScrollPos);
+		findTextField.requestFocus();
+		findTextField.requestFocusInWindow();
+
+	}
+
+	protected void onQuit() {
+		parent.programValues.helpScrollPos = helpScrollPane
+				.getVerticalScrollBar().getValue();
+		if (childHelp != null) {
+			childHelp.onQuit();
+			separateFrame.setVisible(false);
+			separateFrame.dispose();
+		}
+	}
+
+	public void setScrollPos() {
+		parent.programValues.helpScrollPos = helpScrollPane
+				.getVerticalScrollBar().getValue();
+	}
+
+	protected void launchHelpInFrame() {
+		if (invocationCount > 4) {
+			parent.showNotifyMessage(
+					"Yes, you really can launch any number of\nchild help windows. But ... should you? :)",
+					"More Help");
+		}
+		setScrollPos();
+		separateFrame = new JFrame();
+		childHelp = new MyHelpPane(parent, invocationCount);
+		separateFrame.getContentPane().add(childHelp);
+		separateFrame.setSize(getSize());
+		separateFrame.setTitle(parent.frame.getTitle());
+		separateFrame.setIconImage(parent.frame.getIconImage());
+		separateFrame.setVisible(true);
+	}
+
+	String readFile(String path) {
+		StringBuilder sb = new StringBuilder();
+		try {
+			InputStream is = OpticalRayTracer.class.getResourceAsStream(path);
+			InputStreamReader isr = new InputStreamReader(is);
+			BufferedReader br = new BufferedReader(isr);
+			String line;
+			while ((line = br.readLine()) != null) {
+				sb.append(line + "\n");
+			}
+			is.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return sb.toString();
+	}
+
+	// help resource related
+	void setupHelp() {
+		String fn = "/opticalraytracer/helpresources/HelpText.html";
+		try {
+			String s = readFile(fn);
+			s = s.replaceAll("#version#", parent.VERSION);
+			String ud = parent.initManager.userPath.replaceAll("\\\\",
+					"\\\\\\\\");
+			ud = ud.replaceAll("\\$","\\\\\\\\$"); 
+			s = s.replaceAll("#userdir#", ud);
+			helpTextPane.setText(s);
+			helpTextPane.select(0, 0);
+			URL url = parent.getClass().getResource(fn).toURI().toURL();
+			((HTMLDocument) helpTextPane.getDocument()).setBase(url);
+		} catch (Exception e) {
+			System.out.println("setupHelp: " + e);
+			e.printStackTrace();
+		}
+	}
+
+	// manageHyperlinks tries to find and launch a browser
+	void manageHyperlinks(HyperlinkEvent evt) {
+		URL url = evt.getURL();
+		if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+			String surl = evt.getURL().toString();
+			if (surl.matches("http://.*")) {
+				// if (parent.applet) {
+				// parent.setStatus("Applets can't follow hyperlinks");
+				// } else {
+				try {
+					java.awt.Desktop.getDesktop().browse(
+							java.net.URI.create(surl));
+				} catch (Exception e) {
+					System.out.println(e);
+				}
+				// }
+			} else if (surl.matches(".*#.*")) { // bookmark?
+				try {
+					undoPush();
+					helpTextPane.scrollToReference(url.getRef());
+
+				} catch (Exception e) {
+					System.out.println(e);
+				}
+			} else if (surl.matches(".*file:.*")) {
+				String lensSpec = surl.replaceFirst(
+						"(?ms).*?file:\\s*(\\S*?)\\s*", "$1");
+				String s = readFile(lensSpec);
+				parent.setClipboardContents(s);
+			}
+		}
+	}
+
+	void undoPop() {
+		if (undoStack.size() > 0) {
+			redoPush();
+			removeOldHighlight();
+			HelpState hs = undoStack.pop();
+			helpScrollPane.getVerticalScrollBar().setValue(hs.scrollBar);
+			try {
+				oldHighlight = highlighter.addHighlight(hs.selectStart,
+						hs.selectEnd, highlightPainter);
+			} catch (BadLocationException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+			setButtons();
+
+		} else {
+			Common.beep();
+		}
+
+	}
+
+	void redoPop() {
+		if (redoStack.size() > 0) {
+			undoPush();
+			removeOldHighlight();
+			HelpState hs = redoStack.pop();
+			helpScrollPane.getVerticalScrollBar().setValue(hs.scrollBar);
+			try {
+				oldHighlight = highlighter.addHighlight(hs.selectStart,
+						hs.selectEnd, highlightPainter);
+			} catch (BadLocationException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+			setButtons();
+
+		} else {
+			Common.beep();
+		}
+
+	}
+
+	void undoPush() {
+		int ss = 0;
+		int se = 0;
+		Highlight[] ha = highlighter.getHighlights();
+		if (ha.length > 0) {
+			ss = ha[0].getStartOffset();
+			se = ha[0].getEndOffset();
+		}
+		int sbp = helpScrollPane.getVerticalScrollBar().getValue();
+		undoStack.push(new HelpState(sbp, ss, se));
+		setButtons();
+
+	}
+
+	void redoPush() {
+		int ss = 0;
+		int se = 0;
+		Highlight[] ha = highlighter.getHighlights();
+		if (ha.length > 0) {
+			ss = ha[0].getStartOffset();
+			se = ha[0].getEndOffset();
+		}
+		int sbp = helpScrollPane.getVerticalScrollBar().getValue();
+		redoStack.push(new HelpState(sbp, ss, se));
+		setButtons();
+
+	}
+
+	void setButtons() {
+		undoButton.setEnabled(undoStack.size() > 0);
+		redoButton.setEnabled(redoStack.size() > 0);
+	}
+
+	void removeOldHighlight() {
+		if (oldHighlight != null) {
+			highlighter.removeHighlight(oldHighlight);
+			oldHighlight = null;
+		}
+
+	}
+
+	void manageHelpTextField(KeyEvent evt) {
+		// String code = KeyEvent.getKey_Text(evt.getKeyCode());
+		// if a function key, go to main command switchboard
+		// if (code.matches("F\\d")) {
+		// parent.handleKeyPressed(evt);
+		// } else {
+		try {
+
+			removeOldHighlight();
+			doc = helpTextPane.getDocument();
+			int len = doc.getLength();
+			String content = doc.getText(0, len).toLowerCase();
+			String search = findTextField.getText().toLowerCase();
+			if (!search.equals(oldSearch)) {
+				oldPos = 0;
+			}
+
+			oldSearch = search;
+			int p = content.indexOf(search, oldPos);
+			if (p == -1) {
+				oldPos = 0;
+				p = content.indexOf(search, oldPos);
+			}
+
+			if (p >= 0) { // if found
+				int slen = search.length();
+				Rectangle r = helpTextPane.modelToView(p);
+				// aim for the middle of the screen
+				int pos = r.y - helpScrollPane.getHeight() / 2;
+				// but don't try for the impossible
+				pos = (pos < 0) ? 0 : pos;
+				helpScrollPane.getVerticalScrollBar().setValue(pos);
+				// now highlight the found text in our nonfocused text pane
+				oldHighlight = highlighter.addHighlight(p, p + slen,
+						highlightPainter);
+				oldPos = p + 1; // to find next case
+				undoPush();
+			} else {
+				Common.beep();
+			}
+
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+		// }
+
+	}
 }
diff --git a/src/opticalraytracer/MyJFrame.java b/src/opticalraytracer/MyJFrame.java
new file mode 100644
index 0000000..b71ca13
--- /dev/null
+++ b/src/opticalraytracer/MyJFrame.java
@@ -0,0 +1,18 @@
+package opticalraytracer;
+
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.ClipboardOwner;
+import java.awt.datatransfer.Transferable;
+
+import javax.swing.JFrame;
+
+ at SuppressWarnings("serial")
+final public class MyJFrame extends JFrame implements ClipboardOwner {
+
+	@Override
+	public void lostOwnership(Clipboard clipboard, Transferable contents) {
+		// TODO Auto-generated method stub
+		
+	}
+
+}
diff --git a/src/opticalraytracer/ComplexNum.java b/src/opticalraytracer/ObjectValues.java
similarity index 66%
rename from src/opticalraytracer/ComplexNum.java
rename to src/opticalraytracer/ObjectValues.java
index ff08619..4ceab21 100644
--- a/src/opticalraytracer/ComplexNum.java
+++ b/src/opticalraytracer/ObjectValues.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,25 +17,30 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
 
 package opticalraytracer;
 
-/**
- *
- * @author lutusp
- */
-public class ComplexNum {
-
-    double x,y;
-    public ComplexNum(double x, double y) {
-        this.x = x;
-        this.y = y;
-    }
-    public ComplexNum() {
-    }
+final public class ObjectValues extends ValueManager {
+	String name = "";
+	double xPos = 0;
+	double yPos = 0;
+	double lensRadius = 2;
+	double leftSphereRadius = 6;
+	double rightSphereRadius = 6;
+	double thickness = 0; // edge thickness
+	double centerThickness = 0; // a write-only value
+	double ior = 1.52;
+	double leftZValue = 20;
+	double rightZValue = 20;
+	double dispersion = 59;
+	double angle = 0;
+	int leftCurvature = Common.CURVATURE_SPHERICAL;
+	int rightCurvature = Common.CURVATURE_SPHERICAL;
+	boolean symmetrical = true;
+	int function = Common.OBJECT_REFRACTOR;
+	boolean active = true;
 
+	public ObjectValues(String name) {
+		this.name = name;
+	}
 }
diff --git a/src/opticalraytracer/OpticalComponent.java b/src/opticalraytracer/OpticalComponent.java
new file mode 100644
index 0000000..852bb36
--- /dev/null
+++ b/src/opticalraytracer/OpticalComponent.java
@@ -0,0 +1,553 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.*;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+final public class OpticalComponent {
+	OpticalRayTracer parent;
+	ProgramValues programValues;
+	RayTraceComputer rayTraceComputer;
+	ObjectValues values;
+	ArrayList<Vector> opticalTestPolygon;
+	ArrayList<Vector> objectDrawingImage;
+	ArrayList<Vector> mouseProximityPolygon;
+	Vector farFarAway;
+	double objectInsideEpsilon;
+	double internalThickness = 0;
+	double leftInsideRadius = 0;
+	double rightInsideRadius = 0;
+	double drawCount = 32;
+	boolean valid = false;
+	double leftCenter = 0;
+	double rightCenter = 0;
+	private double angleRadians = 0;
+	static final double maxZValue = 1e8;
+	//double epsilon = 1e-8;
+	private ElementBase[] elements = null;
+
+	public OpticalComponent(OpticalRayTracer p, int function) {
+		parent = p;
+		rayTraceComputer = parent.rayTraceComputer;
+		programValues = parent.programValues;
+		String name = nextObjectName(function);
+		values = new ObjectValues(name);
+		setObjectSize(function);
+		valid = true;
+		setup();
+		writeObjectControls();
+	}
+
+	public OpticalComponent(OpticalRayTracer p, int function, String name) {
+		parent = p;
+		rayTraceComputer = parent.rayTraceComputer;
+		programValues = parent.programValues;
+		values = new ObjectValues(name);
+		renameIfRequired();
+		setObjectSize(function);
+		valid = true;
+		setup();
+		writeObjectControls();
+	}
+
+	public OpticalComponent(OpticalRayTracer p, String data, int function) {
+		parent = p;
+		programValues = parent.programValues;
+		rayTraceComputer = parent.rayTraceComputer;
+		String name = nextObjectName(function);
+		values = new ObjectValues(name);
+		setObjectSize(function);
+		valid = values.setValues(data);
+		setup();
+		writeObjectControls();
+	}
+
+	// set up array of optical element types
+
+	protected void setup() {
+		elements = new ElementBase[] { new ElementSpherical(parent),
+				new ElementParabolic(parent), new ElementHyperbolic(parent),
+				new ElementPlanar(parent) };
+		reconfigure();
+	}
+
+	protected void setObjectSize(int function) {
+		values.function = function;
+		if (function == Common.OBJECT_REFLECTOR
+				|| function == Common.OBJECT_ABSORBER) {
+			values.leftCurvature = Common.CURVATURE_PLANAR;
+			values.rightCurvature = Common.CURVATURE_PLANAR;
+			values.thickness = .1;
+		}
+	}
+
+	protected String nextObjectName(int function) {
+		parent.componentNames = new HashSet<>();
+		for (OpticalComponent oc : parent.componentList) {
+			parent.componentNames.add(oc.values.name);
+		}
+		int n = 1;
+		String s = "";
+		while (true) {
+			s = String.format("%s %d", Common.getObjectType(function), n);
+			if (!nameInUse(s)) {
+				break;
+			}
+			n += 1;
+		}
+		return s;
+	}
+
+	protected boolean nameInUse() {
+		return parent.componentNames.contains(values.name);
+	}
+
+	protected boolean nameInUse(String s) {
+		return parent.componentNames.contains(s);
+	}
+
+	protected void renameIfRequired() {
+		if (nameInUse()) {
+			values.name = nextObjectName(values.function);
+		}
+	}
+
+	// begin common access functions
+
+	ElementBase getElement(boolean leftSide) {
+		return elements[leftSide ? values.leftCurvature : values.rightCurvature];
+	}
+
+	protected double xPos() {
+		return values.xPos;
+	}
+
+	protected double yPos() {
+		return values.yPos;
+	}
+
+	protected double sphereRadius(boolean left) {
+		return (left) ? values.leftSphereRadius : values.rightSphereRadius;
+	}
+
+	protected double lensRadius() {
+		return values.lensRadius;
+	}
+
+	protected double offset(boolean left) {
+		return (left) ? leftCenter : rightCenter;
+	}
+
+	protected double zValue(boolean left) {
+		return (left) ? values.leftZValue : values.rightZValue;
+	}
+
+	protected double thickness() {
+		return internalThickness;
+	}
+
+	protected double signedThickness(boolean left) {
+		// left - right +
+		return (left) ? -internalThickness : internalThickness;
+	}
+
+	protected double scale(boolean left) {
+		return abs(sphereRadius(left)) - offset(left);
+	}
+
+	protected double revScale(boolean left) {
+		return abs(sphereRadius(left)) + offset(left);
+	}
+
+	protected double signedScale(boolean left) {
+		return scale(left) * radiusSign(left);
+	}
+
+	protected double radiusSign(boolean left) {
+		return (sphereRadius(left) < 0) ? -1 : 1;
+	}
+
+	protected double sideSign(boolean left) {
+		return (left) ? -1 : 1;
+	}
+
+	protected double angleRadians() {
+		return angleRadians;
+	}
+
+	protected boolean isReflector() {
+		return values.function == Common.OBJECT_REFLECTOR;
+	}
+
+	protected double halfRadius(boolean left) {
+		double sr = abs(sphereRadius(left));
+		double lr = lensRadius();
+		return sr - sqrt(sr * sr - lr * lr);
+	}
+
+	// end common access functions
+
+	protected void reconfigure() {
+		// this is the opposite end of the line used in inside-lens calculations
+		farFarAway = new Vector(1e6, 1e6);
+		limitPositions();
+		// modulo to first circle for consistency
+		// with large entered angles
+		angleRadians = (values.angle % 360.0) * -Common.radians;
+		values.thickness = max(values.thickness, 0);
+		values.ior = max(values.ior, 1);
+		values.lensRadius = abs(values.lensRadius);
+		values.rightZValue = min(values.rightZValue, maxZValue);
+		values.leftZValue = min(values.leftZValue, maxZValue);
+		if (values.symmetrical) {
+			values.rightSphereRadius = values.leftSphereRadius;
+			values.rightZValue = values.leftZValue;
+			values.rightCurvature = values.leftCurvature;
+		}
+		double lsr = checkRadius(values.leftSphereRadius);
+		double rsr = checkRadius(values.rightSphereRadius);
+		boolean lch = lsr != values.leftSphereRadius;
+		boolean rch = rsr != values.rightSphereRadius;
+		parent.leftSphereRadiusTextField.setForeground(lch ? Color.RED
+				: Color.BLACK);
+		parent.rightSphereRadiusTextField.setForeground(rch ? Color.RED
+				: Color.BLACK);
+		parent.lensRadiusTextField.setForeground(lch || rch ? Color.RED
+				: Color.BLACK);
+		values.leftSphereRadius = lsr;
+		values.rightSphereRadius = rsr;
+		leftCenter = sqrt(lsr * lsr - values.lensRadius * values.lensRadius);
+		rightCenter = sqrt(rsr * rsr - values.lensRadius * values.lensRadius);
+		int leftSign = (values.leftSphereRadius < 0) ? -1 : 1;
+		int rightSign = (values.rightSphereRadius < 0) ? -1 : 1;
+		// prevent lens sides from crossing
+		internalThickness = values.thickness / 2;
+		double leftSurface = (abs(lsr) - leftCenter) * leftSign;
+		double rightSurface = (abs(rsr) - rightCenter) * -rightSign;
+		leftSurface += internalThickness;
+		rightSurface -= internalThickness;
+		// an exception to the above if surface is planar
+		if(values.leftCurvature == Common.CURVATURE_PLANAR) leftSurface = internalThickness;
+		if(values.rightCurvature == Common.CURVATURE_PLANAR) rightSurface = -internalThickness;
+		// compute lens center thickness
+		values.centerThickness = max(leftSurface - rightSurface , 0);
+		// values.centerThickness is included in the 
+		// ObjectValues instance only to make it
+		// accessible externally -- setting its value has no effect,
+		// it is a "write-only" value
+		parent.centerThicknessTextField.setText(parent
+				.formatNum(values.centerThickness));
+		//Common.p("" + centerThickness);
+		// compute bias to prevent lens crossover
+		double bias = max(rightSurface - leftSurface, 0);
+		// assert minimum lens thickness
+		internalThickness = internalThickness + bias / 2;
+		// don't allow a lens to be thinner than the surface epsilon
+		// otherwise the algorithm can't distinguish between its sides
+		internalThickness = max(internalThickness,
+				parent.programValues.surfEpsilon);
+		parent.internalThicknessTextField.setText(parent
+				.formatNum(internalThickness * 2));
+		parent.internalThicknessTextField.setForeground((bias > 0 ? Color.RED
+				: Color.BLACK));
+		parent.thicknessTextField.setForeground((bias > 0 ? Color.RED
+				: Color.BLACK));
+		drawCount = 32;
+		double leastRadius = min(abs(values.leftSphereRadius),
+				abs(values.rightSphereRadius));
+		double v = 1024 * pow(values.lensRadius / leastRadius, 3);
+		drawCount = max(drawCount, v);
+		objectInsideEpsilon = parent.programValues.surfEpsilon
+				* sqrt(values.lensRadius);
+		// for comparing with circle-line intersection results
+		opticalTestPolygon = createObjectPerimeter((int) drawCount,
+				objectInsideEpsilon);
+		// for graphic imaging
+		objectDrawingImage = createObjectPerimeter((int) drawCount, 0);
+		// for mouse object detection
+		mouseProximityPolygon = createObjectPerimeter((int) drawCount,
+				objectInsideEpsilon * 200);
+		// showPerimeter(opticalTestPolygon);
+	}
+
+	// this routine establishes whether a point is inside a polygon
+	// and also determines how close the point is to the polygon border
+	// which makes it very slow
+
+	boolean inside(Vector p1, ArrayList<Vector> polygon, double epsilon) {
+		double closest = 1e9;
+		double m;
+		Vector op = null;
+		int crossings = 0;
+		int intersect;
+		for (Vector p : polygon) {
+			if (op != null) {
+				intersect = Common.linesIntersect(farFarAway.x, farFarAway.y,
+						p1.x, p1.y, op.x, op.y, p.x, p.y);
+				m = Common.distanceToParallelLine(p, op, p1);
+				if (!Double.isNaN(m)) {
+					closest = min(closest, abs(m));
+				}
+				switch (intersect) {
+				case -1:
+					return false;
+				case 1:
+					crossings += 1;
+				}
+			}
+			op = p;
+		}
+		// parent.p("closest: " + closest);
+		boolean inside = (((crossings & 1) != 0) && (closest < epsilon));
+		if (inside) {
+			// parent.p("item " + values.name + ", p: " + p1 + " is inside.");
+		}
+		return inside;
+	}
+
+	// this function is much faster, it only determines
+	// that a point is inside a polygon
+	boolean inside(Vector p1, ArrayList<Vector> polygon) {
+		if (p1 == null || Double.isNaN(p1.x) || Double.isNaN(p1.y)) {
+			return false;
+		}
+		Vector op = null;
+		int crossings = 0;
+		int intersect;
+		for (Vector p : polygon) {
+			if (op != null) {
+				intersect = Common.linesIntersect(farFarAway.x, farFarAway.y,
+						p1.x, p1.y, op.x, op.y, p.x, p.y);
+				switch (intersect) {
+				case -1:
+					return false;
+				case 1:
+					crossings += 1;
+				}
+			}
+			op = p;
+		}
+		boolean inside = (crossings & 1) != 0;
+		if (inside) {
+			// parent.p("item " + values.name + ", p: " + p1 + " is inside.");
+		}
+		return inside;
+	}
+
+	// debugging tool
+	void showPerimeter(ArrayList<Vector> points) {
+		StringBuilder sb = new StringBuilder();
+		for (Vector p : points) {
+			sb.append(String.format("(%f,%f),", p.x, p.y));
+		}
+		Common.p(sb.toString());
+	}
+
+	ArrayList<Vector> createObjectPerimeter(int segments, double epsilon) {
+		Vector p;
+		ArrayList<Vector> points = new ArrayList<>();
+		for (int j = 0; j <= 1; j++) {
+			double sgn = (j == 0) ? 1 : -1;
+			for (int i = 0; i <= segments; i++) {
+				double y = values.lensRadius
+						* ((2.0 * i / (double) segments) - 1.0);
+				y *= sgn;
+				p = lensXforY(y, 0);
+				if (p.x < p.y) {
+					p.x -= epsilon;
+					p.y += epsilon;
+				} else {
+					p.x += epsilon;
+					p.y -= epsilon;
+				}
+				Vector ca = new Vector().rotate(p.x, y, angleRadians)
+						.translate(values.xPos, values.yPos);
+				// ca.rotate(p.x, y, angleRadians);
+				// ca.translate(values.xPos, values.yPos);
+				Vector cb = new Vector().rotate(p.y, y, angleRadians)
+						.translate(values.xPos, values.yPos);
+				// cb.rotate(p.y, y, angleRadians);
+				// cb.translate(values.xPos, values.yPos);
+				if (i == 0) {
+					if (j == 0) {
+						points.add(cb);
+					}
+				}
+				if (j == 0) {
+					points.add(ca);
+				} else {
+					points.add(cb);
+				}
+			}
+		}
+		// Collections.reverse(points);
+		return points;
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("object {\n");
+		sb.append(values.getValues());
+		sb.append("}\n");
+		return sb.toString();
+	}
+
+	protected void limitPositions() {
+		values.xPos = min(values.xPos, programValues.virtualSpaceSize);
+		values.xPos = max(values.xPos, -programValues.virtualSpaceSize);
+		values.yPos = min(values.yPos, programValues.virtualSpaceSize);
+		values.yPos = max(values.yPos, -programValues.virtualSpaceSize);
+	}
+
+	double checkRadius(double v) {
+		double sign = (v < 0) ? -1 : 1;
+		v = abs(v);
+		v = max(v, values.lensRadius);
+		return v * sign;
+	}
+
+	protected void setValues(String s) {
+		values.setValues(s);
+	}
+
+	protected String getValues() {
+		return toString();
+	}
+
+	double computeSnap(double v) {
+		double sign = 1;
+		if (programValues.snapValue != 0) {
+			sign = (v < 0) ? -1 : 1;
+			v = abs(v);
+			double q = programValues.snapValue / 2.0;
+			double delta = (v + q) % programValues.snapValue;
+			v = v - delta + q;
+		}
+		return v * sign;
+	}
+
+	// force lens to specified X and Y grid value
+	void snapToGrid() {
+		values.yPos = computeSnap(values.yPos);
+		values.xPos = computeSnap(values.xPos);
+	}
+
+	protected void writeObjectControls() {
+		// parent.p("writeObjectControls1: value: " + values.xPos);
+		for (ControlManager cm : parent.objectControlList.values()) {
+			String tag = cm.getTag();
+			String value = values.getOneValue(tag);
+			cm.setValue(value);
+		}
+		// parent.p("writeObjectControls2: value: " + values.xPos);
+		reconfigure();
+		parent.enableComponentControls(true);
+	}
+
+	protected void readObjectControls() {
+		// parent.p("readObjectControls1: value: " + values.leftCurvature);
+		for (ControlManager cm : parent.objectControlList.values()) {
+			String tag = cm.getTag();
+			String value = cm.getValue();
+			values.setOneValue(tag, value);
+		}
+		// parent.p("readObjectControls2: value: " + values.leftCurvature);
+		reconfigure();
+		parent.enableComponentControls(true);
+		// parent.updateGraphicDisplay();
+	}
+
+	Vector profile(boolean leftSide, double ar) {
+		double lo = (values.leftSphereRadius < 0) ? -leftCenter : leftCenter;
+		double ro = (values.rightSphereRadius < 0) ? -rightCenter : rightCenter;
+		double x = (leftSide) ? values.xPos - lo + internalThickness
+				: values.xPos + ro - internalThickness;
+		Vector v = new Vector(x - values.xPos, 0).rotate(ar).translate(
+				values.xPos, values.yPos);
+		// v.rotate(ar);
+		// v.translate(values.xPos, values.yPos);
+		return v;
+	}
+
+	void computeIntersections(RayLensIntersection oldrli, OpticalComponent oc,
+			boolean leftSide, Vector p1, Vector p2) {
+		ElementBase element = getElement(leftSide);
+
+		element.intersections(oc, leftSide, p1, p2);
+
+	}
+
+	double tangent(boolean leftSide, boolean entering, Vector p, double ar,
+			boolean reflector) {
+
+		Vector rp = p.translate(-values.xPos, -values.yPos).rotate(-ar);
+		// parent.p("tangentA: left: " + leftSide + ", angle: " + ar *
+		// Common.degrees);
+		double dx = getElement(leftSide).lensProfileDXforY(this, leftSide,
+				entering, rp.y);
+		// double sr = this.sphereRadius(leftSide);
+		// Complex r = new Complex(dx/sr,sr).rotate(ar);
+		// p.rotate(ar);
+		// parent.p("tangent B: entering: " + entering + ", left: " + leftSide +
+		// ", y: " + v.y +
+		// ", result: " + qy);
+		return dx;
+	}
+
+	void drawLens(Graphics2D g) {
+		Polygon p = new Polygon();
+		int col = programValues.colorLensOutline;
+		boolean selected = parent.selectedComponent != null
+				&& parent.selectedComponent == this;
+		if (selected) {
+			col = programValues.colorLensSelected;
+		} else if (!values.active) {
+			col = programValues.colorGrid;
+		}
+		MyColor cc = new MyColor(col, 255);
+		g.setColor(cc);
+		boolean drawing = false;
+		ComplexInt i = new ComplexInt();
+		for (Vector pt : objectDrawingImage) {
+			rayTraceComputer.drawScaledLine(pt.x, pt.y, i, g, drawing);
+			rayTraceComputer.addToPolygon(p, pt.x, pt.y);
+			drawing = true;
+		}
+		cc = new MyColor(col);
+		g.setColor(cc);
+		g.fillPolygon(p);
+	}
+
+	Vector lensXforY(double y, double ccx) {
+		// left
+		double a = getElement(true).lensProfileXforY(this, true, y, ccx);
+		// right
+		double b = getElement(false).lensProfileXforY(this, false, y, ccx);
+		return new Vector(a, b);
+	}
+
+}
diff --git a/src/opticalraytracer/OpticalRayTracer.form b/src/opticalraytracer/OpticalRayTracer.form
deleted file mode 100644
index 601e4f5..0000000
--- a/src/opticalraytracer/OpticalRayTracer.form
+++ /dev/null
@@ -1,1314 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" ?>
-
-<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
-  <Events>
-    <EventHandler event="mouseEntered" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="formMouseEntered"/>
-    <EventHandler event="mouseExited" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="formMouseExited"/>
-  </Events>
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,2,31,0,0,3,32"/>
-  </AuxValues>
-
-  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-  <SubComponents>
-    <Container class="javax.swing.JTabbedPane" name="sv_mainTabbedPane">
-      <Properties>
-        <Property name="tabPlacement" type="int" value="3"/>
-      </Properties>
-      <Events>
-        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sv_mainTabbedPaneMouseClicked"/>
-      </Events>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-      </AuxValues>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="1.0"/>
-        </Constraint>
-      </Constraints>
-
-      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
-      <SubComponents>
-        <Container class="javax.swing.JPanel" name="displayTab">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Design">
-                <Property name="tabTitle" type="java.lang.String" value="Design"/>
-                <Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                  <Image iconType="3" name="/opticalraytracer/icons/applications-graphics.png"/>
-                </Property>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="graphicPanel">
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                  <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-              <SubComponents>
-                <Container class="javax.swing.JPanel" name="graphicPlaceholder1">
-                  <Properties>
-                    <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-                      <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
-                        <LineBorder/>
-                      </Border>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="1.0"/>
-                    </Constraint>
-                  </Constraints>
-
-                  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
-                </Container>
-                <Container class="javax.swing.JPanel" name="buttonPanel">
-                  <Properties>
-                    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                      <Color blue="fa" green="dd" red="f7" type="rgb"/>
-                    </Property>
-                    <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-                      <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
-                        <LineBorder/>
-                      </Border>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-
-                  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-                  <SubComponents>
-                    <Component class="javax.swing.JCheckBox" name="sv_antiAliasCheckBox">
-                      <Properties>
-                        <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                          <Color blue="fa" green="dd" red="f7" type="rgb"/>
-                        </Property>
-                        <Property name="selected" type="boolean" value="true"/>
-                        <Property name="text" type="java.lang.String" value="Antialias"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Best appearance, slower drawing"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sv_antiAliasCheckBoxMouseClicked"/>
-                      </Events>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="10" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="2" insetsBottom="0" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JCheckBox" name="sv_invertedCheckBox">
-                      <Properties>
-                        <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                          <Color blue="fa" green="dd" red="f7" type="rgb"/>
-                        </Property>
-                        <Property name="text" type="java.lang.String" value="Inverted"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Dark background"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sv_invertedCheckBoxMouseClicked"/>
-                      </Events>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="8" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="2" insetsBottom="0" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JCheckBox" name="sv_gridCheckBox">
-                      <Properties>
-                        <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                          <Color blue="fa" green="dd" red="f7" type="rgb"/>
-                        </Property>
-                        <Property name="selected" type="boolean" value="true"/>
-                        <Property name="text" type="java.lang.String" value="Grid"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Show grid lines"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sv_gridCheckBoxMouseClicked"/>
-                      </Events>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="9" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="2" insetsBottom="0" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="unselectButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/edit-clear.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Clear present lens selection"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="unselectButtonMouseClicked"/>
-                      </Events>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="5" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="13" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="undoButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/edit-undo.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Undo Prior Action"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="undoButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="6" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="redoButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/edit-redo.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Redo Undone Action"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="redoButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="7" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="newLensButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/document-new.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Create new Lens to right of existing lenses"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="newLensButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="resetButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/process-stop.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Erase & reset all settings"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="resetButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="cPanelButton">
-                      <Properties>
-                        <Property name="text" type="java.lang.String" value="Hide Controls"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Hide control panel below"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="cPanelButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="11" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="2" insetsBottom="0" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="copyImageButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/applications-multimedia.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Copy workspace graphic image to clipboard"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="copyImageButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="copyFullConfigButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/edit-copy.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Copy full configuration to clipboard"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="copyFullConfigButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="pasteFullConfigButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/edit-paste.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Paste full configuration from clipboard"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="pasteFullConfigButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JButton" name="quitButton">
-                      <Properties>
-                        <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                          <Image iconType="3" name="/opticalraytracer/icons/application-exit.png"/>
-                        </Property>
-                        <Property name="toolTipText" type="java.lang.String" value="Quit OpticalRayTracer"/>
-                      </Properties>
-                      <Events>
-                        <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="quitButtonMouseClicked"/>
-                      </Events>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="12" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="2" insetsBottom="0" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                  </SubComponents>
-                </Container>
-              </SubComponents>
-            </Container>
-            <Container class="javax.swing.JPanel" name="controlPanel">
-              <Properties>
-                <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                  <Color blue="fe" green="ee" red="c6" type="rgb"/>
-                </Property>
-                <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-                  <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
-                    <LineBorder/>
-                  </Border>
-                </Property>
-              </Properties>
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                  <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-              <SubComponents>
-                <Component class="javax.swing.JLabel" name="leftLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Left"/>
-                    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[31, 27]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="1.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="rightLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Right"/>
-                    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[40, 27]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="sphereRadiusLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                    <Property name="text" type="java.lang.String" value="Sphere Radius"/>
-                    <Property name="horizontalTextPosition" type="int" value="0"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="cfLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                    <Property name="text" type="java.lang.String" value="Curvature Factor"/>
-                    <Property name="horizontalTextPosition" type="int" value="0"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="leftRadiusTextField">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Monospaced" size="14" style="0"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                    <Property name="toolTipText" type="java.lang.String" value="<html>The radius of an imaginary sphere<br/>&#xa;of which this lens is a part</html>"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="rightRadiusTextField">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Monospaced" size="14" style="0"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                    <Property name="toolTipText" type="java.lang.String" value="<html>The radius of an imaginary sphere<br/>&#xa;of which this lens is a part</html>"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="leftCFTextField">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Monospaced" size="14" style="0"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                    <Property name="toolTipText" type="java.lang.String" value="<html>Hyperboloid curvature factor<br/>&#xa;0 &lt;= f &lt;= 0.25&#xa;</html>&#xa;"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="2" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="rightCFTextField">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Monospaced" size="14" style="0"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                    <Property name="toolTipText" type="java.lang.String" value="<html>Hyperboloid curvature factor<br/>&#xa;0 &lt;= f &lt;= 0.25&#xa;</html>"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="2" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="leftHypCheckBox">
-                  <Properties>
-                    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                      <Color blue="fe" green="ee" red="c6" type="rgb"/>
-                    </Property>
-                    <Property name="toolTipText" type="java.lang.String" value="Select hyperbolic curvature"/>
-                    <Property name="alignmentX" type="float" value="0.5"/>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="leftHypCheckBoxMouseClicked"/>
-                  </Events>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="3" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="rightHypCheckBox">
-                  <Properties>
-                    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                      <Color blue="fe" green="ee" red="c6" type="rgb"/>
-                    </Property>
-                    <Property name="toolTipText" type="java.lang.String" value="Select hyperbolic curvature"/>
-                    <Property name="alignmentX" type="float" value="0.5"/>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="rightHypCheckBoxMouseClicked"/>
-                  </Events>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="3" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="hypLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                    <Property name="text" type="java.lang.String" value="Hyperboloid"/>
-                    <Property name="horizontalTextPosition" type="int" value="0"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Container class="javax.swing.JPanel" name="controlSubPanel">
-                  <Properties>
-                    <Property name="opaque" type="boolean" value="false"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="3" gridWidth="6" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-
-                  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-                  <SubComponents>
-                    <Component class="javax.swing.JLabel" name="iorLabel">
-                      <Properties>
-                        <Property name="horizontalAlignment" type="int" value="0"/>
-                        <Property name="text" type="java.lang.String" value="IOR"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Index of Refraction"/>
-                      </Properties>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="4" insetsBottom="2" insetsRight="4" anchor="17" weightX="0.25" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JTextField" name="iorTextField">
-                      <Properties>
-                        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                          <Font name="Monospaced" size="14" style="0"/>
-                        </Property>
-                        <Property name="horizontalAlignment" type="int" value="4"/>
-                        <Property name="text" type="java.lang.String" value="0.0"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Index of refraction"/>
-                      </Properties>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="5" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JLabel" name="dispersionLabel">
-                      <Properties>
-                        <Property name="horizontalAlignment" type="int" value="0"/>
-                        <Property name="text" type="java.lang.String" value="Dispersion"/>
-                      </Properties>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="6" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="4" insetsBottom="2" insetsRight="4" anchor="17" weightX="0.25" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JTextField" name="dispersionTextField">
-                      <Properties>
-                        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                          <Font name="Monospaced" size="14" style="0"/>
-                        </Property>
-                        <Property name="horizontalAlignment" type="int" value="4"/>
-                        <Property name="text" type="java.lang.String" value="0.0"/>
-                        <Property name="toolTipText" type="java.lang.String" value="<html>A wavelength-sensitive<br/>&#xa;property of some media</html>&#xa;"/>
-                      </Properties>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="7" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="13" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JLabel" name="lensRadiusLabel">
-                      <Properties>
-                        <Property name="horizontalAlignment" type="int" value="0"/>
-                        <Property name="text" type="java.lang.String" value="Lens Radius"/>
-                        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                          <Dimension value="[84, 27]"/>
-                        </Property>
-                      </Properties>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.25" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JTextField" name="lensRadiusTextField">
-                      <Properties>
-                        <Property name="horizontalAlignment" type="int" value="4"/>
-                        <Property name="text" type="java.lang.String" value="0.0"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Center-to-edge radius"/>
-                      </Properties>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JLabel" name="thicknessLabel1">
-                      <Properties>
-                        <Property name="horizontalAlignment" type="int" value="0"/>
-                        <Property name="text" type="java.lang.String" value="Thickness"/>
-                      </Properties>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="4" insetsBottom="2" insetsRight="4" anchor="10" weightX="0.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                    <Component class="javax.swing.JTextField" name="userThicknessTextField">
-                      <Properties>
-                        <Property name="horizontalAlignment" type="int" value="4"/>
-                        <Property name="text" type="java.lang.String" value="0.0"/>
-                        <Property name="toolTipText" type="java.lang.String" value="Lens thickness apart from curvature"/>
-                      </Properties>
-                      <AuxValues>
-                        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                      </AuxValues>
-                      <Constraints>
-                        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                          <GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                        </Constraint>
-                      </Constraints>
-                    </Component>
-                  </SubComponents>
-                </Container>
-                <Component class="javax.swing.JLabel" name="posLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                    <Property name="text" type="java.lang.String" value="Position"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="4" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="xPosLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="X"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="4" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="4" insetsBottom="2" insetsRight="4" anchor="17" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="xPosTextField">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Monospaced" size="14" style="0"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                    <Property name="toolTipText" type="java.lang.String" value="Lens X coordinate"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="5" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="2.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="yPosLabel">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="DejaVu Sans" size="14" style="1"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Y"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="4" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="4" insetsBottom="2" insetsRight="4" anchor="17" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="yPosTextField">
-                  <Properties>
-                    <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-                      <Font name="Monospaced" size="14" style="0"/>
-                    </Property>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                    <Property name="toolTipText" type="java.lang.String" value="Lens Y coordinate"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="5" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="17" weightX="2.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="symmCheckBox">
-                  <Properties>
-                    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                      <Color blue="fe" green="ee" red="c6" type="rgb"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Symmetrical"/>
-                    <Property name="toolTipText" type="java.lang.String" value="Make both lens sides the same"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="symmCheckBoxMouseClicked"/>
-                  </Events>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="configurationTab">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Configure">
-                <Property name="tabTitle" type="java.lang.String" value="Configure"/>
-                <Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                  <Image iconType="3" name="/opticalraytracer/icons/applications-accessories.png"/>
-                </Property>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-          <SubComponents>
-            <Container class="javax.swing.JPanel" name="graphicPlaceholder2">
-              <Properties>
-                <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-                  <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
-                    <LineBorder/>
-                  </Border>
-                </Property>
-              </Properties>
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                  <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="1.0"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
-            </Container>
-            <Container class="javax.swing.JPanel" name="colorPanel">
-              <Properties>
-                <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                  <Color blue="dd" green="fa" red="fe" type="rgb"/>
-                </Property>
-                <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-                  <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
-                    <LineBorder/>
-                  </Border>
-                </Property>
-              </Properties>
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                  <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-              <SubComponents>
-                <Component class="javax.swing.JButton" name="baselineColorButton">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="<html>&nbsp;&nbsp;&nbsp;&nbsp;</html>"/>
-                    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[64, 32]"/>
-                    </Property>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="gridColorButton">
-                  <Properties>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="lensOutlineColorButton">
-                  <Properties>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="beamColorButton">
-                  <Properties>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="5" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="intersectionColorButton">
-                  <Properties>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="6" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="lensSelColorButton">
-                  <Properties>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="7" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="dispHiColorButton">
-                  <Properties>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="8" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="dispLoColorButton">
-                  <Properties>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="9" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel1">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                    <Property name="text" type="java.lang.String" value="Choose colors:"/>
-                    <Property name="toolTipText" type="java.lang.String" value="Hover over buttons for names"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JButton" name="barrierColorButton">
-                  <Properties>
-                    <Property name="text" type="java.lang.String" value="<html>&nbsp;&nbsp;&nbsp;&nbsp;</html>"/>
-                    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[64, 32]"/>
-                    </Property>
-                    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
-                      <Dimension value="[48, 24]"/>
-                    </Property>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-              </SubComponents>
-            </Container>
-            <Container class="javax.swing.JPanel" name="numericPanel">
-              <Properties>
-                <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                  <Color blue="ea" green="f6" red="d1" type="rgb"/>
-                </Property>
-                <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-                  <Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
-                    <LineBorder/>
-                  </Border>
-                </Property>
-              </Properties>
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                  <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                </Constraint>
-              </Constraints>
-
-              <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-              <SubComponents>
-                <Component class="javax.swing.JLabel" name="jLabel2">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Intersection dot size"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_dotRadiusTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                    <Property name="toolTipText" type="java.lang.String" value="Negative radii = solid dots"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel3">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Snap-to-base band"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_snapToBaseTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel4">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Beam width"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_beamWidthTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel5">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Beam count"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_beamCountTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel6">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Interactions per beam"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="2" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_intersectionsTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="3" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel7">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Source Y start"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="4" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_yStartTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="5" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel8">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Source Y end"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_yEndTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel9">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="X source plane"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="2" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_xSourcePlaneTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="3" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel10">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="X target plane"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="4" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_xTargetPlaneTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="5" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel11">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Beam offset angle"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_offsetAngleTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="1" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JLabel" name="jLabel12">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="Dispersion beam count"/>
-                  </Properties>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="2" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="4" insetsBottom="0" insetsRight="4" anchor="10" weightX="0.25" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JTextField" name="sv_dispersionBeamsTextField">
-                  <Properties>
-                    <Property name="horizontalAlignment" type="int" value="4"/>
-                    <Property name="text" type="java.lang.String" value="0.0"/>
-                  </Properties>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="3" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="sv_divergingSourceCheckBox">
-                  <Properties>
-                    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                      <Color blue="ea" green="f6" red="d1" type="rgb"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Diverging beams"/>
-                    <Property name="horizontalAlignment" type="int" value="0"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sv_divergingSourceCheckBoxMouseClicked"/>
-                  </Events>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="6" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="1.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-                <Component class="javax.swing.JCheckBox" name="sv_zeroRotCheckBox">
-                  <Properties>
-                    <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
-                      <Color blue="ea" green="f6" red="d1" type="rgb"/>
-                    </Property>
-                    <Property name="text" type="java.lang.String" value="Rotate from X Origin"/>
-                  </Properties>
-                  <Events>
-                    <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sv_zeroRotCheckBoxMouseClicked"/>
-                  </Events>
-                  <AuxValues>
-                    <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-                  </AuxValues>
-                  <Constraints>
-                    <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-                      <GridBagConstraints gridX="6" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="2" anchor="10" weightX="0.0" weightY="0.0"/>
-                    </Constraint>
-                  </Constraints>
-                </Component>
-              </SubComponents>
-            </Container>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JPanel" name="helpTab">
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
-              <JTabbedPaneConstraints tabName="Help">
-                <Property name="tabTitle" type="java.lang.String" value="Help"/>
-                <Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
-                  <Image iconType="3" name="/opticalraytracer/icons/system-help.png"/>
-                </Property>
-              </JTabbedPaneConstraints>
-            </Constraint>
-          </Constraints>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
-        </Container>
-      </SubComponents>
-    </Container>
-    <Component class="javax.swing.JLabel" name="statusLabel">
-      <Properties>
-        <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
-          <Font name="Monospaced" size="14" style="1"/>
-        </Property>
-        <Property name="text" type="java.lang.String" value="Cursor Position"/>
-      </Properties>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
-      </AuxValues>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="4" insetsBottom="2" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
-        </Constraint>
-      </Constraints>
-    </Component>
-  </SubComponents>
-</Form>
diff --git a/src/opticalraytracer/OpticalRayTracer.java b/src/opticalraytracer/OpticalRayTracer.java
index 0ef88b0..43bc2e0 100644
--- a/src/opticalraytracer/OpticalRayTracer.java
+++ b/src/opticalraytracer/OpticalRayTracer.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,1965 +17,1643 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-/*
- * OpticalRayTracer.java
- *
- * Created on Mar 16, 2009, 8:15:45 AM
- */
+
 package opticalraytracer;
 
-import javax.swing.*;
-import java.util.*;
-import java.awt.*;
-import java.awt.image.*;
+import static java.lang.Math.abs;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Font;
+import java.awt.SystemColor;
+import java.awt.Toolkit;
 import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.ClipboardOwner;
+import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.Transferable;
-import java.text.*;
-import javax.swing.text.html.parser.*;
-import netscape.javascript.*;
-
-/**
- *
- * @author lutusp
- */
-final public class OpticalRayTracer extends javax.swing.JPanel implements ClipboardOwner {
-
-    final String appVersion = "3.2";
-    final String appName;
-    final String programName;
-    JApplet applet = null;
-    JFrame sv_programFrame = null;
-    MyHelpPane helpPane;
-    NumberFormat numberFormat;
-    int sv_helpScrollPos = 0;
-    InitManager initManager = null;
-    //GraphicDisplay graphicDisplay;
-    RayTraceComputer rayTraceComputer;
-    ControlPanelManager controlPanelManager;
-    Stack<UndoRedoPackage> undoStack;
-    Stack<UndoRedoPackage> redoStack;
-    Vector<Lens> sv_lensList;
-    int sv_selectedLensIndex = -1;
-    Lens selectedLens = null;
-    Lens mouseTarget = null;
-    GraphicDisplay gd1, gd2;
-    ColorButton sv_dispHiColor;
-    ColorButton sv_dispLoColor;
-    ColorButton sv_beamColor;
-    ColorButton sv_lensSelColor;
-    ColorButton sv_intersectionColor;
-    ColorButton sv_lensOutlineColor;
-    ColorButton sv_yBaselineColor;
-    ColorButton sv_barrierColor;
-    ColorButton sv_gridColor;
-    double sv_dispScale;
-    double sv_xOffset;
-    double sv_yOffset;
-    double xCenter;
-    double yCenter;
-    double mousePressX, mousePressY;
-    //int currentTab;
-    int intersectionDotRadius;
-    double yStartBeamPos;
-    double yEndBeamPos;
-    int dispersionBeams;
-    int beamCount;
-    int maxIntersections;
-    double xBeamSourceRefPlane;
-    double xBeamTargetRefPlane;
-    double ySnap;
-    double beamAngle;
-    int beamWidth;
-    BufferedImage image = null;
-    int sv_clipboardGraphicXSize;
-    int sv_clipboardGraphicYSize;
-    double textFieldDoubleSensitivity;
-    double textFieldIntSensitivity;
-    double curvatureFactorSensitivity;
-    boolean divergingSource;
-    boolean antiAlias;
-    boolean inverted;
-    boolean drawGrid;
-    boolean rotXZero = false;
-    // END default initializations
-    double sv_lensMinThickness; // % of lens radius
-    double userThickness;
-    int overlappedLensSelector;
-    int xSize;
-    int ySize;
-    int sv_decimalPlaces = 3;
-    MutableDouble dx = new MutableDouble();
-    MutableDouble dy = new MutableDouble();
-    int popupMouseX, popupMouseY;
-
-    /** Creates new form OpticalRayTracer */
-    public OpticalRayTracer(JFrame frame, JApplet applet, String[] args) {
-        this.applet = applet;
-        sv_programFrame = (JFrame) frame;
-        appName = getClass().getSimpleName();
-        // this is essential to avoid a problem with
-        // HTMLEditorKit and applets
-        ParserDelegator workaround = new ParserDelegator();
-        initComponents();
-        // just for testing locale issues
-        //Locale.setDefault(Locale.GERMANY);
-        numberFormat = NumberFormat.getNumberInstance();
-        sv_lensList = new Vector<Lens>();
-        resetUndoRedo();
-
-        //sv_programFrame = this; // used to set screen geometry
-        // Just for the first run
-        int w = 800;
-        int h = 650;
-        Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();
-        //URL url = getClass().getResource(appName + ".class");
-        //appPath = url.getPath().replaceFirst("(.*?)!.*", "$1");
-        //appPath = appPath.replaceFirst("file:", "");
-        //appPath = new File(appPath).getPath();
-        programName = appName + " " + appVersion;
-        if (sv_programFrame != null) {
-            sv_programFrame.setBounds(new Rectangle((ss.width - w) / 2, (ss.height - h) / 2, w, h));
-            //sv_programFrame.setTitle(programName);
-            //sv_programFrame.setIconImage(new ImageIcon(getClass().getResource("icons/" + appName + ".png")).getImage());
-        }
-        //userDir = System.getProperty("user.home");
-        //lineSep = System.getProperty("line.separator");
-        //fileSep = System.getProperty("file.separator");
-        //userPath = userDir + fileSep + "." + appName;
-        // FIXME no need for this reference
-        rayTraceComputer = new RayTraceComputer(this);
-        controlPanelManager = new ControlPanelManager(this, rayTraceComputer);
-        gd1 = new GraphicDisplay(this, rayTraceComputer);
-        graphicPlaceholder1.add(gd1);
-        gd2 = new GraphicDisplay(this, rayTraceComputer);
-        graphicPlaceholder2.add(gd2);
-
-        setDefaults(false);
-
-        setControlActions();
-
-        //setupColorButtons();
-
-        initManager = new InitManager(this, applet != null);
-        if (applet == null) {
-            initManager.readConfig();
-        }
-        this.quitButton.setEnabled(applet == null);
-        this.copyImageButton.setEnabled(applet == null);
-        if (sv_selectedLensIndex != -1 && sv_selectedLensIndex < sv_lensList.size()) {
-            setSelectedLens(sv_lensList.get(sv_selectedLensIndex));
-        }
-
-        helpPane = new MyHelpPane(this);
-        helpTab.add(helpPane);
-
-        readControls();
-        SwingUtilities.invokeLater(new Runnable() {
-
-            public void run() {
-                updateGraphicDisplay();
-            }
-        });
-        makeDefaultLenses(false);
-        // this forces tooltips to be shown in applet mode
-        mouse_present_test(true);
-    }
-
-    public String getConfig() {
-        if (initManager != null) {
-            sv_helpScrollPos = helpPane.getScrollPos();
-            return initManager.getConfig();
-        } else {
-            return null;
-        }
-    }
-
-    public void setConfig(String data) {
-        initManager.setConfig(data);
-        readControls();
-        //enabled = true;
-        //setModeAndLabels();
-        //mainPanel3d.updateSettings();
-        processTabClick();
-        SwingUtilities.invokeLater(
-                new Runnable() {
-
-                    public void run() {
-                        helpPane.setScrollPos(sv_helpScrollPos);
-                    }
-                });
-    }
-
-    // debugging printers
-    public <T> void p(T s) {
-        System.out.println(s);
-    }
-
-    String pn(double n) {
-        return String.format("%.4f", n);
-    }
-
-    void setStatus(String s) {
-        statusLabel.setText(s);
-    }
-
-    void setSelectedLens(Lens p) {
-        controlPanelManager.readLensValues(p);
-        selectedLens = p;
-    }
-
-    void resetUndoRedo() {
-        undoStack = new Stack<UndoRedoPackage>();
-        redoStack = new Stack<UndoRedoPackage>();
-        updateUndoRedoButtons();
-    }
-
-    void pushUndo() {
-        //p("pushundo: " + selectedLens + "," + mouseTarget);
-        undoStack.push(new UndoRedoPackage(sv_lensList, selectedLens));
-        updateUndoRedoButtons();
-    }
-
-    void pushRedo() {
-        // p("pushredo: " + selectedLens + "," + mouseTarget);
-        redoStack.push(new UndoRedoPackage(sv_lensList, selectedLens));
-        updateUndoRedoButtons();
-    }
-
-    void undo() {
-        if (undoStack.size() > 0) {
-            pushRedo();
-            undoStack.pop().restore(this);
-            //p("undo: " + selectedLens + "," + mouseTarget);
-            updateGraphicDisplay();
-            updateUndoRedoButtons();
-        } else {
-            beep();
-        }
-    }
-
-    void redo() {
-        if (redoStack.size() > 0) {
-            pushUndo();
-            redoStack.pop().restore(this);
-            //p("redo: " + selectedLens + "," + mouseTarget);
-            updateGraphicDisplay();
-            updateUndoRedoButtons();
-        } else {
-            beep();
-        }
-    }
-
-    void updateUndoRedoButtons() {
-        redoButton.setEnabled(redoStack.size() > 0);
-        undoButton.setEnabled(undoStack.size() > 0);
-    }
-
-    static void beep() {
-        Toolkit.getDefaultToolkit().beep();
-    }
-
-    String formatNum(double v) {
-        return String.format("%." + sv_decimalPlaces + "f", v);
-    }
-
-    void setControlActions() {
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, lensRadiusTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, userThicknessTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, leftRadiusTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, rightRadiusTextField, this);
-        new UserActionManager(curvatureFactorSensitivity, 0, 0.25, leftCFTextField, this);
-        new UserActionManager(curvatureFactorSensitivity, 0, 0.25, rightCFTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, iorTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, dispersionTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, xPosTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, yPosTextField, this);
-
-        new UserActionManager(textFieldIntSensitivity, -1e10, 1e10, sv_dotRadiusTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, 0, 1e10, sv_snapToBaseTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, sv_beamWidthTextField, this);
-        new UserActionManager(textFieldIntSensitivity, 2, 1e10, sv_beamCountTextField, this);
-        new UserActionManager(textFieldIntSensitivity, 0, 1e10, sv_intersectionsTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, sv_yStartTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, sv_yEndTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, sv_xSourcePlaneTextField, this);
-        new UserActionManager(textFieldDoubleSensitivity, -1e10, 1e10, sv_xTargetPlaneTextField, this);
-        new UserActionManager(textFieldIntSensitivity, -1e10, 1e10, sv_offsetAngleTextField, this);
-        new UserActionManager(textFieldIntSensitivity, 0, 1e10, sv_dispersionBeamsTextField, this);
-    }
-
-    public void updateGraphicDisplay() {
-        gd1.updateDisplay();
-        gd2.updateDisplay();
-    }
-
-    public void unSelectLens() {
-        pushUndo();
-        selectedLens = null;
-        updateGraphicDisplay();
-    }
-
-    Lens makeGenericLens(double cx, double cy, double radius, double thickness) {
-        double ior = 1.52;
-        double disp = 59;
-        double cf = 0.03;
-        boolean leftHyp = false;
-        boolean rightHyp = false;
-        boolean symmetrical = true;
-        return new Lens(this, this.rayTraceComputer, cx, cy, 2, radius, radius, thickness, ior, cf, cf, disp, leftHyp, rightHyp, symmetrical);
-    }
-
-    // create two generic default lenses
-    void makeDefaultLenses(boolean update) {
-        if (sv_lensList.size() == 0) {
-            sv_lensList.add(makeGenericLens(0, 0, 6, 0));
-            sv_lensList.add(makeGenericLens(4, 0, -6, 0.1));
-            selectedLens = null;
-            if (update) {
-                updateGraphicDisplay();
-            }
-        }
-    }
-
-    void makeNewLens() {
-        pushUndo();
-        double cx = 0;
-        Iterator<Lens> it = sv_lensList.iterator();
-        while (it.hasNext()) {
-            double tx = it.next().cx;
-            cx = Math.max(cx, tx);
-        }
-        cx += 1.0;
-        Lens newLens = makeGenericLens(cx, 0, 6, 0);
-        sv_lensList.add(newLens);
-        setSelectedLens(newLens);
-        gd1.snapLens(selectedLens);
-        updateGraphicDisplay();
-    }
-
-    void makeNewLensPopup() {
-        pushUndo();
-        rayTraceComputer.displayToSpace(popupMouseX, popupMouseY, dx, dy);
-        double cx = dx.v + sv_xOffset;
-        double cy = dy.v + sv_yOffset;
-        Lens lens = makeGenericLens(cx, cy, 6, 0);
-        sv_lensList.add(lens);
-        setSelectedLens(lens);
-        gd1.snapLens(lens);
-        updateGraphicDisplay();
-    }
-
-    void deleteSelectedLens() {
-        if (selectedLens != null) {
-            if (showMessage("Okay to delete selected lens?", "Delete Lens")) {
-                sv_lensList.remove(selectedLens);
-                selectedLens = null;
-                updateGraphicDisplay();
-            }
-        }
-    }
-
-    void setupColorButtons() {
-        sv_yBaselineColor = new ColorButton(this, baselineColorButton, 0x004000, "X/Y Baseline");
-        sv_gridColor = new ColorButton(this, gridColorButton, 0xc0c0c0, "Grid");
-        sv_lensOutlineColor = new ColorButton(this, lensOutlineColorButton, 0x80a0ff, "Lens outline");
-        sv_intersectionColor = new ColorButton(this, intersectionColorButton, 0xaa00aa, "Beam intersection");
-        sv_dispHiColor = new ColorButton(this, dispHiColorButton, 0xffffff, "High background");
-        sv_dispLoColor = new ColorButton(this, dispLoColorButton, 0x000000, "Low background");
-        sv_lensSelColor = new ColorButton(this, lensSelColorButton, 0x00c000, "Lens-selected");
-        sv_beamColor = new ColorButton(this, beamColorButton, 0xff0000, "Tracing beams");
-        sv_barrierColor = new ColorButton(this, barrierColorButton, 0x0000ff, "Domain barrier");
-    }
-
-    void setDefaults(boolean newLenses) {
-        sv_dispScale = 0.2;
-        sv_xOffset = 2;
-        sv_yOffset = 0;
-        xCenter = 0;
-        yCenter = 0;
-        intersectionDotRadius = 4;
-        yStartBeamPos = -1.8;
-        yEndBeamPos = 1.8;
-        dispersionBeams = 0;
-        beamCount = 6;
-        maxIntersections = 64;
-        xBeamSourceRefPlane = -30.0;
-        xBeamTargetRefPlane = 30.0;
-        ySnap = 1.0;
-        beamAngle = 0;
-        beamWidth = 1;
-        sv_clipboardGraphicXSize = 1280;
-        sv_clipboardGraphicYSize = 1024;
-        textFieldDoubleSensitivity = 0.1;
-        textFieldIntSensitivity = 1;
-        curvatureFactorSensitivity = 0.001;
-        divergingSource = false;
-        antiAlias = true;
-        inverted = false;
-        drawGrid = true;
-        sv_lensMinThickness = .0001; // % of lens radius
-        userThickness = 0;
-        overlappedLensSelector = 0;
-        xSize = -1;
-        ySize = -1;
-        sv_decimalPlaces = 4;
-        sv_invertedCheckBox.setSelected(false);
-        sv_gridCheckBox.setSelected(true);
-        sv_antiAliasCheckBox.setSelected(true);
-        resetUndoRedo();
-        setupColorButtons();
-        if (newLenses) {
-            sv_lensList = new Vector<Lens>();
-            makeDefaultLenses(true);
-        }
-        writeControls();
-    }
-
-    double getDouble(String s) {
-        double v = 0;
-        double e;
-        try {
-            // deal with possibility of exponent
-            // which numberformat cannot process
-            s = s.toLowerCase();
-            s = s.replaceFirst("\\s*(.*?)\\s*", "$1");
-            if (!s.matches("\\(.*")) {
-                String[] array = s.split("e");
-                v = numberFormat.parse(array[0]).doubleValue();
-                if (array.length > 1) {
-                    // get signed exponent
-                    e = numberFormat.parse(array[1]).doubleValue();
-                    v *= Math.pow(10, e);
-                }
-            }
-        } catch (Exception ex) {
-            System.out.println(getClass().getName() + ".getDouble: Error: " + ex + ", source: " + s);
-            //ex.printStackTrace();
-        }
-        return v;
-    }
-
-    double readJTextField(JTextField tf, double result) {
-        try {
-            result = getDouble(tf.getText());
-        } catch (Exception e) {
-            System.out.println(getClass().getName() + ": readJTextField Error: " + e);
-        }
-        return result;
-    }
-
-    int readJTextField(JTextField tf, int result) {
-        try {
-            result = (int) getDouble(tf.getText());
-        } catch (Exception e) {
-            System.out.println(getClass().getName() + ": readJTextField Error: " + e);
-        }
-        return result;
-    }
-
-    void writeJTextField(JTextField tf, int v) {
-        tf.setText("" + v);
-    }
-
-    void writeJTextField(JTextField tf, double v) {
-        tf.setText(formatNum(v));
-    }
-
-    void readControls() {
-        intersectionDotRadius = readJTextField(sv_dotRadiusTextField, intersectionDotRadius);
-        ySnap = readJTextField(sv_snapToBaseTextField, ySnap);
-        beamWidth = readJTextField(sv_beamWidthTextField, beamWidth);
-        beamCount = readJTextField(sv_beamCountTextField, beamCount);
-        maxIntersections = readJTextField(sv_intersectionsTextField, maxIntersections);
-        yStartBeamPos = readJTextField(sv_yStartTextField, yStartBeamPos);
-        yEndBeamPos = readJTextField(sv_yEndTextField, yEndBeamPos);
-        xBeamSourceRefPlane = readJTextField(sv_xSourcePlaneTextField, xBeamSourceRefPlane);
-        xBeamTargetRefPlane = readJTextField(sv_xTargetPlaneTextField, xBeamTargetRefPlane);
-        beamAngle = readJTextField(sv_offsetAngleTextField, beamAngle);
-        dispersionBeams = readJTextField(sv_dispersionBeamsTextField, dispersionBeams);
-        divergingSource = sv_divergingSourceCheckBox.isSelected();
-        drawGrid = sv_gridCheckBox.isSelected();
-        antiAlias = sv_antiAliasCheckBox.isSelected();
-        inverted = sv_invertedCheckBox.isSelected();
-        rotXZero = sv_zeroRotCheckBox.isSelected();
-        updateGraphicDisplay();
-    }
-
-    void writeControls() {
-        writeJTextField(sv_dotRadiusTextField, intersectionDotRadius);
-        writeJTextField(sv_snapToBaseTextField, ySnap);
-        writeJTextField(sv_beamWidthTextField, beamWidth);
-        writeJTextField(sv_beamCountTextField, beamCount);
-        writeJTextField(sv_intersectionsTextField, maxIntersections);
-        writeJTextField(sv_yStartTextField, yStartBeamPos);
-        writeJTextField(sv_yEndTextField, yEndBeamPos);
-        writeJTextField(sv_xSourcePlaneTextField, xBeamSourceRefPlane);
-        writeJTextField(sv_xTargetPlaneTextField, xBeamTargetRefPlane);
-        writeJTextField(sv_offsetAngleTextField, beamAngle);
-        writeJTextField(sv_dispersionBeamsTextField, dispersionBeams);
-        sv_divergingSourceCheckBox.setSelected(divergingSource);
-        sv_gridCheckBox.setSelected(drawGrid);
-        sv_antiAliasCheckBox.setSelected(antiAlias);
-        sv_invertedCheckBox.setSelected(inverted);
-        sv_zeroRotCheckBox.setSelected(rotXZero);
-
-    }
-
-    void eraseReset() {
-        if (showMessage("Okay to reset to defaults (erases all changes)?", "To Defaults")) {
-            setDefaults(true);
-        }
-    }
-
-    void showHideControls() {
-        if (controlPanel.isVisible()) {
-            controlPanel.setVisible(false);
-            cPanelButton.setText("Show Controls");
-        } else {
-            controlPanel.setVisible(true);
-            cPanelButton.setText("Hide Controls");
-        }
-    }
-
-    boolean showMessage(String message, String title) {
-        int reply = JOptionPane.showConfirmDialog(this, message, programName + ": " + title, JOptionPane.YES_NO_CANCEL_OPTION);
-        return (reply == JOptionPane.YES_OPTION);
-    }
-
-    void clipboardCopyImage() {
-        if (applet == null) {
-            this.gd1.rayTraceProcessCore(sv_clipboardGraphicXSize, this.sv_clipboardGraphicYSize);
-            ImageTransferable imt = new ImageTransferable(image);
-            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
-            clipboard.setContents(imt, null);
-        }
-    }
-
-    void clipboardCopyFullConfig() {
-        String config = this.initManager.getFullConfig();
-        setClipboardContents(config);
-    }
-
-    void clipboardPasteFullConfig() {
-        String data = getClipboardContents();
-        if (data != null) {
-            if (showMessage("Okay to read full configuration (erases all current settings)?", "Read Full Configuration")) {
-                initManager.setConfig(data);
-                readControls();
-            }
-        }
-    }
-
-    void setClipboardContents(String s) {
-        if (applet != null) {
-            try {
-                //this.applet_clipboard_textpane.setText(s);
-                JSObject window = JSObject.getWindow(applet);
-                JSObject doc = (JSObject) window.getMember("document");
-                JSObject form = (JSObject) doc.getMember("clipform");
-                JSObject field = (JSObject) form.getMember("cliparea");
-                field.setMember("value", s);
-            } catch (Exception e) {
-                System.out.println("setClipboardContents: " + e);
-            }
-        } else {
-            StringSelection stringSelection = new StringSelection(s);
-            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
-            clipboard.setContents(stringSelection, this);
-        }
-    }
-
-    String getClipboardContents() {
-        String s = null;
-        if (applet != null) {
-            try {
-                JSObject window = JSObject.getWindow(applet);
-                JSObject doc = (JSObject) window.getMember("document");
-                JSObject form = (JSObject) doc.getMember("clipform");
-                JSObject field = (JSObject) form.getMember("cliparea");
-                s = (String) field.getMember("value");
-            } catch (Exception e) {
-                System.out.println("getClipboardContents1: " + e);
-            }
-        } else {
-            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
-            Transferable content = clipboard.getContents(this);
-            if (content != null) {
-                try {
-                    s = (String) content.getTransferData(DataFlavor.stringFlavor);
-                } catch (Exception e) {
-                    System.out.println("getClipboardContents2: " + e);
-                }
-            }
-        }
-        return s;
-    }
-
-    void clipboardCutLens() {
-        Lens lens;
-        if ((lens = clipboardCopyLens()) != null) {
-            pushUndo();
-            sv_lensList.remove(lens);
-            selectedLens = null;
-            updateGraphicDisplay();
-        }
-    }
-
-    Lens clipboardCopyLens() {
-        Lens lens = null;
-        if (selectedLens != null) {
-            lens = selectedLens;
-            setClipboardContents(lens.toString());
-        }
-        return lens;
-    }
-
-    void clipboardPasteLens(boolean toMouse) {
-        String s = getClipboardContents();
-        if (s != null) {
-            if (s.matches("(?ms).*(,|\\s).*")) {
-                decodeMultiLensString(s);
-            } else {
-                Lens newLens = new Lens(this, rayTraceComputer, s);
-                if (newLens.valid) {
-                    pushUndo();
-                    sv_lensList.add(newLens);
-                    if (toMouse) {
-                        // position lens at mouse cursor
-                        rayTraceComputer.displayToSpace(popupMouseX, popupMouseY, dx, dy);
-                        newLens.cx = dx.v + sv_xOffset;
-                        newLens.cy = dy.v + sv_yOffset;
-                    }
-                    updateGraphicDisplay();
-                    // select this lens
-                    setSelectedLens(newLens);
-                    gd1.snapLens(selectedLens);
-                }
-            }
-        }
-    }
-
-    void decodeMultiLensString(String s) {
-        s = s.replaceFirst("(?ms)^(\\s|\\[|\"|')*(.*?)(\\s|\\]|\"|')*$", "$2");
-        s = s.replaceAll("(\\s|,)+", ",");
-        String[] array = s.split(",");
-        for (int i = 0; i < array.length; i++) {
-            sv_lensList.add(new Lens(this, rayTraceComputer, array[i]));
-        }
-        updateGraphicDisplay();
-    }
-
-    public void lostOwnership(Clipboard aClipboard, Transferable aContents) {
-        //do nothing
-    }
-
-    void processTabClick() {
-        Component comp = sv_mainTabbedPane.getSelectedComponent();
-        //comp.requestFocus();
-        if (comp == helpTab) {
-            helpPane.sv_findTextField.requestFocusInWindow();
-        }
-    }
-
-    // this allows tooltips to be displayed
-    // in the applet mode after a mouse exit/enter
-    public void mouse_present_test(boolean present) {
-        if (present) {
-            this.requestFocus();
-        }
-
-    }
-
-    public void close() {
-        if (applet == null) {
-            this.sv_helpScrollPos = helpPane.getScrollPos();
-            sv_selectedLensIndex = sv_lensList.indexOf(selectedLens);
-            initManager.writeConfig();
-        }
-        setVisible(false);
-        System.exit(0);
-    }
-
-    /** This method is called from within the constructor to
-     * initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is
-     * always regenerated by the Form Editor.
-     */
-    @SuppressWarnings("unchecked")
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-        java.awt.GridBagConstraints gridBagConstraints;
-
-        sv_mainTabbedPane = new javax.swing.JTabbedPane();
-        displayTab = new javax.swing.JPanel();
-        graphicPanel = new javax.swing.JPanel();
-        graphicPlaceholder1 = new javax.swing.JPanel();
-        buttonPanel = new javax.swing.JPanel();
-        sv_antiAliasCheckBox = new javax.swing.JCheckBox();
-        sv_invertedCheckBox = new javax.swing.JCheckBox();
-        sv_gridCheckBox = new javax.swing.JCheckBox();
-        unselectButton = new javax.swing.JButton();
-        undoButton = new javax.swing.JButton();
-        redoButton = new javax.swing.JButton();
-        newLensButton = new javax.swing.JButton();
-        resetButton = new javax.swing.JButton();
-        cPanelButton = new javax.swing.JButton();
-        copyImageButton = new javax.swing.JButton();
-        copyFullConfigButton = new javax.swing.JButton();
-        pasteFullConfigButton = new javax.swing.JButton();
-        quitButton = new javax.swing.JButton();
-        controlPanel = new javax.swing.JPanel();
-        leftLabel = new javax.swing.JLabel();
-        rightLabel = new javax.swing.JLabel();
-        sphereRadiusLabel = new javax.swing.JLabel();
-        cfLabel = new javax.swing.JLabel();
-        leftRadiusTextField = new javax.swing.JTextField();
-        rightRadiusTextField = new javax.swing.JTextField();
-        leftCFTextField = new javax.swing.JTextField();
-        rightCFTextField = new javax.swing.JTextField();
-        leftHypCheckBox = new javax.swing.JCheckBox();
-        rightHypCheckBox = new javax.swing.JCheckBox();
-        hypLabel = new javax.swing.JLabel();
-        controlSubPanel = new javax.swing.JPanel();
-        iorLabel = new javax.swing.JLabel();
-        iorTextField = new javax.swing.JTextField();
-        dispersionLabel = new javax.swing.JLabel();
-        dispersionTextField = new javax.swing.JTextField();
-        lensRadiusLabel = new javax.swing.JLabel();
-        lensRadiusTextField = new javax.swing.JTextField();
-        thicknessLabel1 = new javax.swing.JLabel();
-        userThicknessTextField = new javax.swing.JTextField();
-        posLabel = new javax.swing.JLabel();
-        xPosLabel = new javax.swing.JLabel();
-        xPosTextField = new javax.swing.JTextField();
-        yPosLabel = new javax.swing.JLabel();
-        yPosTextField = new javax.swing.JTextField();
-        symmCheckBox = new javax.swing.JCheckBox();
-        configurationTab = new javax.swing.JPanel();
-        graphicPlaceholder2 = new javax.swing.JPanel();
-        colorPanel = new javax.swing.JPanel();
-        baselineColorButton = new javax.swing.JButton();
-        gridColorButton = new javax.swing.JButton();
-        lensOutlineColorButton = new javax.swing.JButton();
-        beamColorButton = new javax.swing.JButton();
-        intersectionColorButton = new javax.swing.JButton();
-        lensSelColorButton = new javax.swing.JButton();
-        dispHiColorButton = new javax.swing.JButton();
-        dispLoColorButton = new javax.swing.JButton();
-        jLabel1 = new javax.swing.JLabel();
-        barrierColorButton = new javax.swing.JButton();
-        numericPanel = new javax.swing.JPanel();
-        jLabel2 = new javax.swing.JLabel();
-        sv_dotRadiusTextField = new javax.swing.JTextField();
-        jLabel3 = new javax.swing.JLabel();
-        sv_snapToBaseTextField = new javax.swing.JTextField();
-        jLabel4 = new javax.swing.JLabel();
-        sv_beamWidthTextField = new javax.swing.JTextField();
-        jLabel5 = new javax.swing.JLabel();
-        sv_beamCountTextField = new javax.swing.JTextField();
-        jLabel6 = new javax.swing.JLabel();
-        sv_intersectionsTextField = new javax.swing.JTextField();
-        jLabel7 = new javax.swing.JLabel();
-        sv_yStartTextField = new javax.swing.JTextField();
-        jLabel8 = new javax.swing.JLabel();
-        sv_yEndTextField = new javax.swing.JTextField();
-        jLabel9 = new javax.swing.JLabel();
-        sv_xSourcePlaneTextField = new javax.swing.JTextField();
-        jLabel10 = new javax.swing.JLabel();
-        sv_xTargetPlaneTextField = new javax.swing.JTextField();
-        jLabel11 = new javax.swing.JLabel();
-        sv_offsetAngleTextField = new javax.swing.JTextField();
-        jLabel12 = new javax.swing.JLabel();
-        sv_dispersionBeamsTextField = new javax.swing.JTextField();
-        sv_divergingSourceCheckBox = new javax.swing.JCheckBox();
-        sv_zeroRotCheckBox = new javax.swing.JCheckBox();
-        helpTab = new javax.swing.JPanel();
-        statusLabel = new javax.swing.JLabel();
-
-        addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseEntered(java.awt.event.MouseEvent evt) {
-                formMouseEntered(evt);
-            }
-            public void mouseExited(java.awt.event.MouseEvent evt) {
-                formMouseExited(evt);
-            }
-        });
-        setLayout(new java.awt.GridBagLayout());
-
-        sv_mainTabbedPane.setTabPlacement(javax.swing.JTabbedPane.BOTTOM);
-        sv_mainTabbedPane.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                sv_mainTabbedPaneMouseClicked(evt);
-            }
-        });
-
-        displayTab.setLayout(new java.awt.GridBagLayout());
-
-        graphicPanel.setLayout(new java.awt.GridBagLayout());
-
-        graphicPlaceholder1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-        graphicPlaceholder1.setLayout(new java.awt.BorderLayout());
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.weighty = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        graphicPanel.add(graphicPlaceholder1, gridBagConstraints);
-
-        buttonPanel.setBackground(new java.awt.Color(247, 221, 250));
-        buttonPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-        buttonPanel.setLayout(new java.awt.GridBagLayout());
-
-        sv_antiAliasCheckBox.setBackground(new java.awt.Color(247, 221, 250));
-        sv_antiAliasCheckBox.setSelected(true);
-        sv_antiAliasCheckBox.setText("Antialias");
-        sv_antiAliasCheckBox.setToolTipText("Best appearance, slower drawing");
-        sv_antiAliasCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                sv_antiAliasCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 10;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
-        buttonPanel.add(sv_antiAliasCheckBox, gridBagConstraints);
-
-        sv_invertedCheckBox.setBackground(new java.awt.Color(247, 221, 250));
-        sv_invertedCheckBox.setText("Inverted");
-        sv_invertedCheckBox.setToolTipText("Dark background");
-        sv_invertedCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                sv_invertedCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 8;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
-        buttonPanel.add(sv_invertedCheckBox, gridBagConstraints);
-
-        sv_gridCheckBox.setBackground(new java.awt.Color(247, 221, 250));
-        sv_gridCheckBox.setSelected(true);
-        sv_gridCheckBox.setText("Grid");
-        sv_gridCheckBox.setToolTipText("Show grid lines");
-        sv_gridCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                sv_gridCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 9;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
-        buttonPanel.add(sv_gridCheckBox, gridBagConstraints);
-
-        unselectButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-clear.png"))); // NOI18N
-        unselectButton.setToolTipText("Clear present lens selection");
-        unselectButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                unselectButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 5;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(unselectButton, gridBagConstraints);
-
-        undoButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-undo.png"))); // NOI18N
-        undoButton.setToolTipText("Undo Prior Action");
-        undoButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                undoButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 6;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(undoButton, gridBagConstraints);
-
-        redoButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-redo.png"))); // NOI18N
-        redoButton.setToolTipText("Redo Undone Action");
-        redoButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                redoButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 7;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(redoButton, gridBagConstraints);
-
-        newLensButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/document-new.png"))); // NOI18N
-        newLensButton.setToolTipText("Create new Lens to right of existing lenses");
-        newLensButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                newLensButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(newLensButton, gridBagConstraints);
-
-        resetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/process-stop.png"))); // NOI18N
-        resetButton.setToolTipText("Erase & reset all settings");
-        resetButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                resetButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(resetButton, gridBagConstraints);
-
-        cPanelButton.setText("Hide Controls");
-        cPanelButton.setToolTipText("Hide control panel below");
-        cPanelButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                cPanelButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 11;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
-        buttonPanel.add(cPanelButton, gridBagConstraints);
-
-        copyImageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/applications-multimedia.png"))); // NOI18N
-        copyImageButton.setToolTipText("Copy workspace graphic image to clipboard");
-        copyImageButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                copyImageButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(copyImageButton, gridBagConstraints);
-
-        copyFullConfigButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-copy.png"))); // NOI18N
-        copyFullConfigButton.setToolTipText("Copy full configuration to clipboard");
-        copyFullConfigButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                copyFullConfigButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(copyFullConfigButton, gridBagConstraints);
-
-        pasteFullConfigButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/edit-paste.png"))); // NOI18N
-        pasteFullConfigButton.setToolTipText("Paste full configuration from clipboard");
-        pasteFullConfigButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                pasteFullConfigButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        buttonPanel.add(pasteFullConfigButton, gridBagConstraints);
-
-        quitButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/application-exit.png"))); // NOI18N
-        quitButton.setToolTipText("Quit OpticalRayTracer");
-        quitButton.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                quitButtonMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 12;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 2, 0, 2);
-        buttonPanel.add(quitButton, gridBagConstraints);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        graphicPanel.add(buttonPanel, gridBagConstraints);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.weighty = 1.0;
-        displayTab.add(graphicPanel, gridBagConstraints);
-
-        controlPanel.setBackground(new java.awt.Color(198, 238, 254));
-        controlPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-        controlPanel.setLayout(new java.awt.GridBagLayout());
-
-        leftLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        leftLabel.setText("Left");
-        leftLabel.setMinimumSize(new java.awt.Dimension(31, 27));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.weighty = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
-        controlPanel.add(leftLabel, gridBagConstraints);
-
-        rightLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        rightLabel.setText("Right");
-        rightLabel.setMinimumSize(new java.awt.Dimension(40, 27));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
-        controlPanel.add(rightLabel, gridBagConstraints);
-
-        sphereRadiusLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        sphereRadiusLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        sphereRadiusLabel.setText("Sphere Radius");
-        sphereRadiusLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
-        controlPanel.add(sphereRadiusLabel, gridBagConstraints);
-
-        cfLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        cfLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        cfLabel.setText("Curvature Factor");
-        cfLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
-        controlPanel.add(cfLabel, gridBagConstraints);
-
-        leftRadiusTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        leftRadiusTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        leftRadiusTextField.setText("0.0");
-        leftRadiusTextField.setToolTipText("<html>The radius of an imaginary sphere<br/>\nof which this lens is a part</html>");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlPanel.add(leftRadiusTextField, gridBagConstraints);
-
-        rightRadiusTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        rightRadiusTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        rightRadiusTextField.setText("0.0");
-        rightRadiusTextField.setToolTipText("<html>The radius of an imaginary sphere<br/>\nof which this lens is a part</html>");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlPanel.add(rightRadiusTextField, gridBagConstraints);
-
-        leftCFTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        leftCFTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        leftCFTextField.setText("0.0");
-        leftCFTextField.setToolTipText("<html>Hyperboloid curvature factor<br/>\n0 <= f <= 0.25\n</html>\n");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlPanel.add(leftCFTextField, gridBagConstraints);
-
-        rightCFTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        rightCFTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        rightCFTextField.setText("0.0");
-        rightCFTextField.setToolTipText("<html>Hyperboloid curvature factor<br/>\n0 <= f <= 0.25\n</html>");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlPanel.add(rightCFTextField, gridBagConstraints);
-
-        leftHypCheckBox.setBackground(new java.awt.Color(198, 238, 254));
-        leftHypCheckBox.setToolTipText("Select hyperbolic curvature");
-        leftHypCheckBox.setAlignmentX(0.5F);
-        leftHypCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        leftHypCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                leftHypCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        controlPanel.add(leftHypCheckBox, gridBagConstraints);
-
-        rightHypCheckBox.setBackground(new java.awt.Color(198, 238, 254));
-        rightHypCheckBox.setToolTipText("Select hyperbolic curvature");
-        rightHypCheckBox.setAlignmentX(0.5F);
-        rightHypCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        rightHypCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                rightHypCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        controlPanel.add(rightHypCheckBox, gridBagConstraints);
-
-        hypLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        hypLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        hypLabel.setText("Hyperboloid");
-        hypLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
-        controlPanel.add(hypLabel, gridBagConstraints);
-
-        controlSubPanel.setOpaque(false);
-        controlSubPanel.setLayout(new java.awt.GridBagLayout());
-
-        iorLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        iorLabel.setText("IOR");
-        iorLabel.setToolTipText("Index of Refraction");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(2, 4, 2, 4);
-        controlSubPanel.add(iorLabel, gridBagConstraints);
-
-        iorTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        iorTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        iorTextField.setText("0.0");
-        iorTextField.setToolTipText("Index of refraction");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 5;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlSubPanel.add(iorTextField, gridBagConstraints);
-
-        dispersionLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        dispersionLabel.setText("Dispersion");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 6;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(2, 4, 2, 4);
-        controlSubPanel.add(dispersionLabel, gridBagConstraints);
-
-        dispersionTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        dispersionTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        dispersionTextField.setText("0.0");
-        dispersionTextField.setToolTipText("<html>A wavelength-sensitive<br/>\nproperty of some media</html>\n");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 7;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlSubPanel.add(dispersionTextField, gridBagConstraints);
-
-        lensRadiusLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        lensRadiusLabel.setText("Lens Radius");
-        lensRadiusLabel.setMinimumSize(new java.awt.Dimension(84, 27));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
-        controlSubPanel.add(lensRadiusLabel, gridBagConstraints);
-
-        lensRadiusTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        lensRadiusTextField.setText("0.0");
-        lensRadiusTextField.setToolTipText("Center-to-edge radius");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlSubPanel.add(lensRadiusTextField, gridBagConstraints);
-
-        thicknessLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        thicknessLabel1.setText("Thickness");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.insets = new java.awt.Insets(2, 4, 2, 4);
-        controlSubPanel.add(thicknessLabel1, gridBagConstraints);
-
-        userThicknessTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        userThicknessTextField.setText("0.0");
-        userThicknessTextField.setToolTipText("Lens thickness apart from curvature");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlSubPanel.add(userThicknessTextField, gridBagConstraints);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 3;
-        gridBagConstraints.gridwidth = 6;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        controlPanel.add(controlSubPanel, gridBagConstraints);
-
-        posLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        posLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        posLabel.setText("Position");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.gridwidth = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
-        controlPanel.add(posLabel, gridBagConstraints);
-
-        xPosLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        xPosLabel.setText("X");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(2, 4, 2, 4);
-        controlPanel.add(xPosLabel, gridBagConstraints);
-
-        xPosTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        xPosTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        xPosTextField.setText("0.0");
-        xPosTextField.setToolTipText("Lens X coordinate");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 5;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 2.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlPanel.add(xPosTextField, gridBagConstraints);
-
-        yPosLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 14));
-        yPosLabel.setText("Y");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(2, 4, 2, 4);
-        controlPanel.add(yPosLabel, gridBagConstraints);
-
-        yPosTextField.setFont(new java.awt.Font("Monospaced", 0, 14));
-        yPosTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        yPosTextField.setText("0.0");
-        yPosTextField.setToolTipText("Lens Y coordinate");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 5;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 2.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        controlPanel.add(yPosTextField, gridBagConstraints);
-
-        symmCheckBox.setBackground(new java.awt.Color(198, 238, 254));
-        symmCheckBox.setText("Symmetrical");
-        symmCheckBox.setToolTipText("Make both lens sides the same");
-        symmCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                symmCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 0);
-        controlPanel.add(symmCheckBox, gridBagConstraints);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        displayTab.add(controlPanel, gridBagConstraints);
-
-        sv_mainTabbedPane.addTab("Design", new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/applications-graphics.png")), displayTab); // NOI18N
-
-        configurationTab.setLayout(new java.awt.GridBagLayout());
-
-        graphicPlaceholder2.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-        graphicPlaceholder2.setLayout(new java.awt.BorderLayout());
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.weighty = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        configurationTab.add(graphicPlaceholder2, gridBagConstraints);
-
-        colorPanel.setBackground(new java.awt.Color(254, 250, 221));
-        colorPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-        colorPanel.setLayout(new java.awt.GridBagLayout());
-
-        baselineColorButton.setText("<html>    </html>");
-        baselineColorButton.setMinimumSize(new java.awt.Dimension(64, 32));
-        baselineColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(baselineColorButton, gridBagConstraints);
-
-        gridColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(gridColorButton, gridBagConstraints);
-
-        lensOutlineColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(lensOutlineColorButton, gridBagConstraints);
-
-        beamColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 5;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(beamColorButton, gridBagConstraints);
-
-        intersectionColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 6;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(intersectionColorButton, gridBagConstraints);
-
-        lensSelColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 7;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(lensSelColorButton, gridBagConstraints);
-
-        dispHiColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 8;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(dispHiColorButton, gridBagConstraints);
-
-        dispLoColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 9;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(dispLoColorButton, gridBagConstraints);
-
-        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        jLabel1.setText("Choose colors:");
-        jLabel1.setToolTipText("Hover over buttons for names");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        colorPanel.add(jLabel1, gridBagConstraints);
-
-        barrierColorButton.setText("<html>    </html>");
-        barrierColorButton.setMinimumSize(new java.awt.Dimension(64, 32));
-        barrierColorButton.setPreferredSize(new java.awt.Dimension(48, 24));
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        colorPanel.add(barrierColorButton, gridBagConstraints);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        configurationTab.add(colorPanel, gridBagConstraints);
-
-        numericPanel.setBackground(new java.awt.Color(209, 246, 234));
-        numericPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
-        numericPanel.setLayout(new java.awt.GridBagLayout());
-
-        jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel2.setText("Intersection dot size");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel2, gridBagConstraints);
-
-        sv_dotRadiusTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_dotRadiusTextField.setText("0.0");
-        sv_dotRadiusTextField.setToolTipText("Negative radii = solid dots");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_dotRadiusTextField, gridBagConstraints);
-
-        jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel3.setText("Snap-to-base band");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel3, gridBagConstraints);
-
-        sv_snapToBaseTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_snapToBaseTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_snapToBaseTextField, gridBagConstraints);
-
-        jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel4.setText("Beam width");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel4, gridBagConstraints);
-
-        sv_beamWidthTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_beamWidthTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_beamWidthTextField, gridBagConstraints);
-
-        jLabel5.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel5.setText("Beam count");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel5, gridBagConstraints);
-
-        sv_beamCountTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_beamCountTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_beamCountTextField, gridBagConstraints);
-
-        jLabel6.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel6.setText("Interactions per beam");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel6, gridBagConstraints);
-
-        sv_intersectionsTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_intersectionsTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_intersectionsTextField, gridBagConstraints);
-
-        jLabel7.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel7.setText("Source Y start");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel7, gridBagConstraints);
-
-        sv_yStartTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_yStartTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 5;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_yStartTextField, gridBagConstraints);
-
-        jLabel8.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel8.setText("Source Y end");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel8, gridBagConstraints);
-
-        sv_yEndTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_yEndTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_yEndTextField, gridBagConstraints);
-
-        jLabel9.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel9.setText("X source plane");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel9, gridBagConstraints);
-
-        sv_xSourcePlaneTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_xSourcePlaneTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_xSourcePlaneTextField, gridBagConstraints);
-
-        jLabel10.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel10.setText("X target plane");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 4;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel10, gridBagConstraints);
-
-        sv_xTargetPlaneTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_xTargetPlaneTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 5;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_xTargetPlaneTextField, gridBagConstraints);
-
-        jLabel11.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel11.setText("Beam offset angle");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 3;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel11, gridBagConstraints);
-
-        sv_offsetAngleTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_offsetAngleTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 1;
-        gridBagConstraints.gridy = 3;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_offsetAngleTextField, gridBagConstraints);
-
-        jLabel12.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
-        jLabel12.setText("Dispersion beam count");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 2;
-        gridBagConstraints.gridy = 3;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 0.25;
-        gridBagConstraints.insets = new java.awt.Insets(0, 4, 0, 4);
-        numericPanel.add(jLabel12, gridBagConstraints);
-
-        sv_dispersionBeamsTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
-        sv_dispersionBeamsTextField.setText("0.0");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 3;
-        gridBagConstraints.gridy = 3;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        numericPanel.add(sv_dispersionBeamsTextField, gridBagConstraints);
-
-        sv_divergingSourceCheckBox.setBackground(new java.awt.Color(209, 246, 234));
-        sv_divergingSourceCheckBox.setText("Diverging beams");
-        sv_divergingSourceCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
-        sv_divergingSourceCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                sv_divergingSourceCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 6;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        numericPanel.add(sv_divergingSourceCheckBox, gridBagConstraints);
-
-        sv_zeroRotCheckBox.setBackground(new java.awt.Color(209, 246, 234));
-        sv_zeroRotCheckBox.setText("Rotate from X Origin");
-        sv_zeroRotCheckBox.addMouseListener(new java.awt.event.MouseAdapter() {
-            public void mouseClicked(java.awt.event.MouseEvent evt) {
-                sv_zeroRotCheckBoxMouseClicked(evt);
-            }
-        });
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 6;
-        gridBagConstraints.gridy = 0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        numericPanel.add(sv_zeroRotCheckBox, gridBagConstraints);
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 2;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
-        configurationTab.add(numericPanel, gridBagConstraints);
-
-        sv_mainTabbedPane.addTab("Configure", new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/applications-accessories.png")), configurationTab); // NOI18N
-
-        helpTab.setLayout(new java.awt.BorderLayout());
-        sv_mainTabbedPane.addTab("Help", new javax.swing.ImageIcon(getClass().getResource("/opticalraytracer/icons/system-help.png")), helpTab); // NOI18N
-
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.weighty = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
-        add(sv_mainTabbedPane, gridBagConstraints);
-
-        statusLabel.setFont(new java.awt.Font("Monospaced", 1, 14));
-        statusLabel.setText("Cursor Position");
-        gridBagConstraints = new java.awt.GridBagConstraints();
-        gridBagConstraints.gridx = 0;
-        gridBagConstraints.gridy = 1;
-        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(2, 4, 2, 0);
-        add(statusLabel, gridBagConstraints);
-    }// </editor-fold>//GEN-END:initComponents
-
-    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
-        // TODO add your handling code here:
-        close();
-    }//GEN-LAST:event_formWindowClosing
-
-    private void symmCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_symmCheckBoxMouseClicked
-        // TODO add your handling code here:
-        updateGraphicDisplay();
-    }//GEN-LAST:event_symmCheckBoxMouseClicked
-
-    private void leftHypCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_leftHypCheckBoxMouseClicked
-        // TODO add your handling code here:
-        updateGraphicDisplay();
-}//GEN-LAST:event_leftHypCheckBoxMouseClicked
-
-    private void rightHypCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_rightHypCheckBoxMouseClicked
-        // TODO add your handling code here:
-        updateGraphicDisplay();
-    }//GEN-LAST:event_rightHypCheckBoxMouseClicked
-
-    private void sv_antiAliasCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sv_antiAliasCheckBoxMouseClicked
-        // TODO add your handling code here:
-        readControls();
-}//GEN-LAST:event_sv_antiAliasCheckBoxMouseClicked
-
-    private void sv_invertedCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sv_invertedCheckBoxMouseClicked
-        // TODO add your handling code here:
-        readControls();
-}//GEN-LAST:event_sv_invertedCheckBoxMouseClicked
-
-    private void sv_gridCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sv_gridCheckBoxMouseClicked
-        // TODO add your handling code here:
-        readControls();
-}//GEN-LAST:event_sv_gridCheckBoxMouseClicked
-
-    private void unselectButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_unselectButtonMouseClicked
-        if (((JButton) evt.getSource()).isEnabled()) {
-            unSelectLens();
-        }
-    }//GEN-LAST:event_unselectButtonMouseClicked
-
-    private void undoButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_undoButtonMouseClicked
-        // TODO add your handling code here:
-        undo();
-    }//GEN-LAST:event_undoButtonMouseClicked
-
-    private void redoButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_redoButtonMouseClicked
-        // TODO add your handling code here:
-        redo();
-    }//GEN-LAST:event_redoButtonMouseClicked
-
-    private void newLensButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_newLensButtonMouseClicked
-        // TODO add your handling code here:
-        makeNewLens();
-    }//GEN-LAST:event_newLensButtonMouseClicked
-
-    private void resetButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_resetButtonMouseClicked
-        // TODO add your handling code here:
-        eraseReset();
-    }//GEN-LAST:event_resetButtonMouseClicked
-
-    private void cPanelButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_cPanelButtonMouseClicked
-        // TODO add your handling code here:
-        showHideControls();
-    }//GEN-LAST:event_cPanelButtonMouseClicked
-
-    private void sv_divergingSourceCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sv_divergingSourceCheckBoxMouseClicked
-        // TODO add your handling code here:
-        readControls();
-    }//GEN-LAST:event_sv_divergingSourceCheckBoxMouseClicked
-
-    private void copyImageButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_copyImageButtonMouseClicked
-        if (((JButton) evt.getSource()).isEnabled()) {
-            clipboardCopyImage();
-        }
-    }//GEN-LAST:event_copyImageButtonMouseClicked
-
-    private void copyFullConfigButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_copyFullConfigButtonMouseClicked
-        // TODO add your handling code here:
-        clipboardCopyFullConfig();
-    }//GEN-LAST:event_copyFullConfigButtonMouseClicked
-
-    private void pasteFullConfigButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_pasteFullConfigButtonMouseClicked
-        // TODO add your handling code here:
-        clipboardPasteFullConfig();
-    }//GEN-LAST:event_pasteFullConfigButtonMouseClicked
-
-    private void quitButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_quitButtonMouseClicked
-        if (((JButton) evt.getSource()).isEnabled()) {
-            close();
-        }
-    }//GEN-LAST:event_quitButtonMouseClicked
-
-    private void sv_mainTabbedPaneMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sv_mainTabbedPaneMouseClicked
-        // TODO add your handling code here:
-        processTabClick();
-    }//GEN-LAST:event_sv_mainTabbedPaneMouseClicked
-
-    private void formMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseEntered
-        mouse_present_test(true);
-    }//GEN-LAST:event_formMouseEntered
-
-    private void formMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseExited
-        mouse_present_test(false);
-    }//GEN-LAST:event_formMouseExited
-
-    private void sv_zeroRotCheckBoxMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sv_zeroRotCheckBoxMouseClicked
-        readControls();
-    }//GEN-LAST:event_sv_zeroRotCheckBoxMouseClicked
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    private javax.swing.JButton barrierColorButton;
-    private javax.swing.JButton baselineColorButton;
-    private javax.swing.JButton beamColorButton;
-    private javax.swing.JPanel buttonPanel;
-    private javax.swing.JButton cPanelButton;
-    private javax.swing.JLabel cfLabel;
-    private javax.swing.JPanel colorPanel;
-    private javax.swing.JPanel configurationTab;
-    private javax.swing.JPanel controlPanel;
-    private javax.swing.JPanel controlSubPanel;
-    private javax.swing.JButton copyFullConfigButton;
-    private javax.swing.JButton copyImageButton;
-    private javax.swing.JButton dispHiColorButton;
-    private javax.swing.JButton dispLoColorButton;
-    private javax.swing.JLabel dispersionLabel;
-    protected javax.swing.JTextField dispersionTextField;
-    private javax.swing.JPanel displayTab;
-    private javax.swing.JPanel graphicPanel;
-    private javax.swing.JPanel graphicPlaceholder1;
-    private javax.swing.JPanel graphicPlaceholder2;
-    private javax.swing.JButton gridColorButton;
-    private javax.swing.JPanel helpTab;
-    private javax.swing.JLabel hypLabel;
-    private javax.swing.JButton intersectionColorButton;
-    private javax.swing.JLabel iorLabel;
-    protected javax.swing.JTextField iorTextField;
-    private javax.swing.JLabel jLabel1;
-    private javax.swing.JLabel jLabel10;
-    private javax.swing.JLabel jLabel11;
-    private javax.swing.JLabel jLabel12;
-    private javax.swing.JLabel jLabel2;
-    private javax.swing.JLabel jLabel3;
-    private javax.swing.JLabel jLabel4;
-    private javax.swing.JLabel jLabel5;
-    private javax.swing.JLabel jLabel6;
-    private javax.swing.JLabel jLabel7;
-    private javax.swing.JLabel jLabel8;
-    private javax.swing.JLabel jLabel9;
-    protected javax.swing.JTextField leftCFTextField;
-    protected javax.swing.JCheckBox leftHypCheckBox;
-    private javax.swing.JLabel leftLabel;
-    protected javax.swing.JTextField leftRadiusTextField;
-    private javax.swing.JButton lensOutlineColorButton;
-    private javax.swing.JLabel lensRadiusLabel;
-    protected javax.swing.JTextField lensRadiusTextField;
-    private javax.swing.JButton lensSelColorButton;
-    private javax.swing.JButton newLensButton;
-    private javax.swing.JPanel numericPanel;
-    private javax.swing.JButton pasteFullConfigButton;
-    private javax.swing.JLabel posLabel;
-    private javax.swing.JButton quitButton;
-    private javax.swing.JButton redoButton;
-    private javax.swing.JButton resetButton;
-    protected javax.swing.JTextField rightCFTextField;
-    protected javax.swing.JCheckBox rightHypCheckBox;
-    private javax.swing.JLabel rightLabel;
-    protected javax.swing.JTextField rightRadiusTextField;
-    private javax.swing.JLabel sphereRadiusLabel;
-    protected javax.swing.JLabel statusLabel;
-    protected javax.swing.JCheckBox sv_antiAliasCheckBox;
-    protected javax.swing.JTextField sv_beamCountTextField;
-    protected javax.swing.JTextField sv_beamWidthTextField;
-    protected javax.swing.JTextField sv_dispersionBeamsTextField;
-    protected javax.swing.JCheckBox sv_divergingSourceCheckBox;
-    protected javax.swing.JTextField sv_dotRadiusTextField;
-    protected javax.swing.JCheckBox sv_gridCheckBox;
-    protected javax.swing.JTextField sv_intersectionsTextField;
-    protected javax.swing.JCheckBox sv_invertedCheckBox;
-    protected javax.swing.JTabbedPane sv_mainTabbedPane;
-    protected javax.swing.JTextField sv_offsetAngleTextField;
-    protected javax.swing.JTextField sv_snapToBaseTextField;
-    protected javax.swing.JTextField sv_xSourcePlaneTextField;
-    protected javax.swing.JTextField sv_xTargetPlaneTextField;
-    protected javax.swing.JTextField sv_yEndTextField;
-    protected javax.swing.JTextField sv_yStartTextField;
-    protected javax.swing.JCheckBox sv_zeroRotCheckBox;
-    protected javax.swing.JCheckBox symmCheckBox;
-    private javax.swing.JLabel thicknessLabel1;
-    private javax.swing.JButton undoButton;
-    protected javax.swing.JButton unselectButton;
-    protected javax.swing.JTextField userThicknessTextField;
-    private javax.swing.JLabel xPosLabel;
-    protected javax.swing.JTextField xPosTextField;
-    private javax.swing.JLabel yPosLabel;
-    protected javax.swing.JTextField yPosTextField;
-    // End of variables declaration//GEN-END:variables
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.ButtonGroup;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.JToolBar;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.miginfocom.swing.MigLayout;
+import java.awt.Component;
+import javax.swing.UIManager;
+import javax.swing.border.CompoundBorder;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.Point;
+
+final public class OpticalRayTracer {
+
+	protected String appName;
+	protected String VERSION = "9.0";
+	protected String fullAppName;
+	double textFieldDoubleSensitivity;
+	double textFieldIntSensitivity;
+	double curvatureFactorSensitivity;
+	ProgramValues programValues;
+	NumberFormat numberFormat;
+	HashMap<String, ControlManager> objectControlList;
+	HashMap<String, ProgramControl> programControlList;
+	ArrayList<JRadioButton> typeRadioButtonList;
+	ArrayList<JRadioButton> leftCurvatureRadioButtonList;
+	ArrayList<JRadioButton> rightCurvatureRadioButtonList;
+	Stack<String> undoStack;
+	Stack<String> redoStack;
+	int undoRedoMaxStack = 128;
+	int maxLightRays = 1000;
+	InitializationManager initManager;
+	BufferedImage image = null;
+	int xSize = -1;
+	int ySize = -1;
+	int xCenter = -1;
+	int yCenter = -1;
+	OpticalComponent mouseTarget = null;
+	OpticalComponent selectedComponent = null;
+	ArrayList<OpticalComponent> componentList;
+	HashSet<String> componentNames;
+	double mousePressX, mousePressY;
+	int popupMouseX = -1;
+	int popupMouseY = -1;
+	int overlappedLensSelector = 0;
+	RayTraceComputer rayTraceComputer;
+	LineAnalysis lineAnalysis;
+	GraphicDisplay gPaneDesign, gPaneConfigure;
+	double lensMinThickness = .0001; // % of lens radius
+	boolean undoFlag = true;
+	boolean redoFlag = true;
+	boolean suppressCombo = false;
+
+	MyHelpPane helpPanel;
+	JScrollPane tableScrollPane;
+	DataTableDisplay dataTableDisplay;
+
+	MyJFrame frame;
+
+	JTextField leftSphereRadiusTextField;
+	JTextField rightSphereRadiusTextField;
+	private JTextField leftZTextField;
+	private JTextField rightZTextField;
+	private JTextField xPosTextField;
+	private JTextField yPosTextField;
+	JTextField lensRadiusTextField;
+	JTextField thicknessTextField;
+	private JTextField iorTextField;
+	private JTextField dispersionTextField;
+	private JTextField angleTextField;
+	private JCheckBox symmetricalCheckBox;
+	private JCheckBox inverseCheckBox;
+	private JCheckBox gridCheckBox;
+	private JCheckBox antiAliasCheckBox;
+	private JCheckBox showControlsCheckBox;
+	private JPanel lensDesignControlPane;
+	protected JButton unselectButton;
+	private JToolBar colorToolBar;
+	private JPanel statusBar;
+	JLabel statusLabel;
+	private JPanel configurePane;
+	private JPanel designPane;
+	JTabbedPane tabbedPane;
+	private JTextField arrowSizeTextField;
+	private JTextField snapValueTextField;
+	private JTextField beamWidthTextField;
+	private JTextField beamCountTextField;
+	private JTextField interactionsTextField;
+	private JTextField yStartTextField;
+	private JTextField beamOffsetTextField;
+	private JTextField dispersionCountTextField;
+	private JTextField yEndTextField;
+	private JTextField xSourcePlaneTextField;
+	private JTextField xTargetPlaneTextField;
+	private JCheckBox divergingBeamsCheckBox;
+	private JButton redoButton;
+	private JButton undoButton;
+	private JButton button;
+	private JLabel lblSurfaceEpsilon;
+	private JTextField interLensEpsilonTextField;
+	private JCheckBox activeCheckBox;
+	private JButton newMIrrorButton;
+	private JRadioButton refractRadioButton;
+	private JRadioButton reflectRadioButton;
+	private JRadioButton absorbRadioButton;
+	protected final ButtonGroup typeButtonGroup = new ButtonGroup();
+	private JLabel lblName;
+	private JTextField nameTextField;
+	private JLabel lblLensEpsilon;
+	private JTextField surfaceEpsilonTextField;
+	private JPanel tablePane;
+
+	private JPanel tableControlPane;
+	private JTextField lineLimitTextField;
+	JLabel tableDataLabel;
+	private JButton btnCopyHtml;
+	private JLabel lblSpaceBoxLimit;
+	private JTextField spaceBoxLimitTextField;
+	private JPanel helpPane;
+	private JLabel lblEffectiveThickness;
+	JTextField internalThicknessTextField;
+	protected final ButtonGroup leftCurvButtonGroup = new ButtonGroup();
+	protected final ButtonGroup rightCurvButtonGroup = new ButtonGroup();
+	private JComboBox<String> leftCurvComboBox;
+	private JComboBox<String> rightCurvComboBox;
+	JTextField centerThicknessTextField;
+	private JLabel lblCenterThickness;
+
+	/**
+	 * Create the application.
+	 * 
+	 * @param args
+	 */
+	public OpticalRayTracer(String[] args) {
+		// just for testing locale issues
+		// Locale.setDefault(Locale.GERMANY);
+		// undocumented logging feature
+		undoStack = new Stack<>();
+		redoStack = new Stack<>();
+		numberFormat = NumberFormat.getNumberInstance();
+		appName = getClass().getSimpleName();
+		fullAppName = appName + " " + VERSION;
+		componentList = new ArrayList<>();
+		componentNames = new HashSet<>();
+		programValues = new ProgramValues();
+		textFieldDoubleSensitivity = .1;
+		textFieldIntSensitivity = 1;
+		curvatureFactorSensitivity = 0.001;
+		rayTraceComputer = new RayTraceComputer(this);
+		lineAnalysis = new LineAnalysis(this);
+		initialize();
+
+		JRadioButton[] trb = new JRadioButton[] { refractRadioButton,
+				reflectRadioButton, absorbRadioButton };
+		// this allows numerical indexing of the radio buttons
+		typeRadioButtonList = new ArrayList<>(Arrays.asList(trb));
+		
+		gPaneDesign = new GraphicDisplay(this, "design pane", Common.TAB_DESIGN);
+		designPane.add(gPaneDesign, SwingConstants.CENTER);
+		gPaneConfigure = new GraphicDisplay(this, "configure pane",
+				Common.TAB_CONFIGURE);
+		configurePane.add(gPaneConfigure, SwingConstants.CENTER);
+		frame.addWindowFocusListener(new WindowAdapter() {
+			public void windowGainedFocus(WindowEvent e) {
+				gPaneDesign.requestFocusInWindow();
+			}
+		});
+		initManager = new InitializationManager(this, programValues);
+
+		dataTableDisplay = new DataTableDisplay(this);
+		dataTableDisplay.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+		tableScrollPane = new JScrollPane(dataTableDisplay,
+				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		tableScrollPane.getViewport().setBackground(Color.white);
+		tableControlPane = new JPanel();
+		tablePane.add(tableScrollPane, BorderLayout.CENTER);
+		tablePane.add(tableControlPane, BorderLayout.SOUTH);
+		tableControlPane.setLayout(new MigLayout("", "[117px][][][grow][]",
+				"[25px]"));
+
+		JButton copyTableButton = new JButton("Copy Data");
+		copyTableButton.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseClicked(MouseEvent e) {
+				copyLineList(false);
+			}
+		});
+		copyTableButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/document-save.png")));
+		copyTableButton
+				.setToolTipText("Copy a full tab-separated data table to the system clipboard");
+		tableControlPane
+				.add(copyTableButton, "cell 0 0,alignx left,aligny top");
+
+		btnCopyHtml = new JButton("Copy HTML");
+		btnCopyHtml.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseClicked(MouseEvent e) {
+				copyLineList(true);
+			}
+		});
+		btnCopyHtml
+				.setToolTipText("Copy a Web-formatted full data table to the system clipboard");
+		btnCopyHtml.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/text-html.png")));
+		tableControlPane.add(btnCopyHtml, "cell 1 0");
+
+		JLabel lblNewLabel_2 = new JLabel("Line limit:");
+		tableControlPane.add(lblNewLabel_2, "cell 2 0,alignx trailing");
+
+		lineLimitTextField = new JTextField();
+		lineLimitTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lineLimitTextField
+				.setToolTipText("A limit to the number of displayed lines (to prevent slow operation).");
+		tableControlPane.add(lineLimitTextField, "cell 3 0");
+		lineLimitTextField.setColumns(10);
+
+		tableDataLabel = new JLabel("");
+		tableControlPane.add(tableDataLabel, "flowx,cell 4 0");
+		setupOpticalControlFields();
+		programControlList = new HashMap<>();
+		setupProgramControlFields();
+		setupColorButtons();
+		ImageIcon programIcon = new ImageIcon(
+				OpticalRayTracer.class
+						.getResource("/opticalraytracer/icons/OpticalRayTracer.png"));
+		frame.setIconImage(programIcon.getImage());
+		frame.setTitle(fullAppName);
+
+		resetButtonColors();
+		helpPane = new JPanel();
+		tabbedPane
+				.addTab("Help",
+						new ImageIcon(
+								OpticalRayTracer.class
+										.getResource("/opticalraytracer/icons/system-help.png")),
+						helpPane, "Show the help document");
+		helpPane.setLayout(new BorderLayout(0, 0));
+		helpPanel = new MyHelpPane(this, 0);
+		helpPane.add(helpPanel);
+		tabbedPane.setMnemonicAt(Common.TAB_DESIGN, KeyEvent.VK_D);
+		tabbedPane.setMnemonicAt(Common.TAB_CONFIGURE, KeyEvent.VK_C);
+		tabbedPane.setMnemonicAt(Common.TAB_TABLE, KeyEvent.VK_T);
+		tabbedPane.setMnemonicAt(Common.TAB_HELP, KeyEvent.VK_H);
+		clearSelection();
+		initManager.readConfig();
+		frame.setBounds(programValues.windowX, programValues.windowY,
+				programValues.defaultWindowWidth,
+				programValues.defaultWindowHeight);
+		writeProgramControls();
+		writeElementControls();
+		resetUndoRedo();
+		setSelectedComponent(programValues.selectedComponent);
+		tabbedPane.setSelectedIndex(programValues.selectedTab);
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				processTabChange();
+			}
+		});
+		
+	}
+	
+	void p(String s) {
+		System.out.println(s);
+	}
+
+	void setupOpticalControlFields() {
+		ControlManager[] array = new ControlManager[] {
+				// The tag names must correspond to declared field names
+				// in the ComponentValues class
+				new ControlManager(nameTextField, this, "name"),
+				new ControlManager(textFieldDoubleSensitivity, 0, 1e10,
+						lensRadiusTextField, this, "lensRadius"),
+				new ControlManager(textFieldDoubleSensitivity, 0, 1e10,
+						thicknessTextField, this, "thickness"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						leftSphereRadiusTextField, this, "leftSphereRadius"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						rightSphereRadiusTextField, this, "rightSphereRadius"),
+				new ControlManager(textFieldDoubleSensitivity, 0,
+						OpticalComponent.maxZValue, leftZTextField, this,
+						"leftZValue"),
+				new ControlManager(textFieldDoubleSensitivity, 0,
+						OpticalComponent.maxZValue, rightZTextField, this,
+						"rightZValue"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						iorTextField, this, "ior"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						dispersionTextField, this, "dispersion"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						xPosTextField, this, "xPos"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						xPosTextField, this, "xPos"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						yPosTextField, this, "yPos"),
+				new ControlManager(1, -1e10, 1e10, angleTextField, this,
+						"angle"),
+				new ControlManager(symmetricalCheckBox, this, "symmetrical"),
+				new ControlManager(activeCheckBox, this, "active"),
+				new ControlManager(refractRadioButton, this, "function"),
+				new ControlManager(reflectRadioButton, this, "function"),
+				new ControlManager(absorbRadioButton, this, "function"),
+				new ControlManager(leftCurvComboBox, this, "leftCurvature"),
+				new ControlManager(rightCurvComboBox, this, "rightCurvature"),
+		};
+		objectControlList = new HashMap<>();
+		for (ControlManager cm : array) {
+			objectControlList.put(cm.getTag(), cm);
+		}
+	}
+
+	void setupProgramControlFields() {
+		ControlManager[] array = new ControlManager[] {
+				// The tag names must correspond to declared field names
+				// in the ProgramValues class
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						arrowSizeTextField, this, "intersectionArrowSize"),
+				new ControlManager(textFieldDoubleSensitivity, 0, 1e10,
+						snapValueTextField, this, "snapValue"),
+				new ControlManager(textFieldIntSensitivity, 1, 1000,
+						beamWidthTextField, this, "beamWidth"),
+				new ControlManager(textFieldIntSensitivity, 1, maxLightRays,
+						beamCountTextField, this, "beamCount"),
+				new ControlManager(textFieldDoubleSensitivity, 0, 1e10,
+						interLensEpsilonTextField, this, "interLensEpsilon"),
+				new ControlManager(textFieldDoubleSensitivity, 0, 1e10,
+						surfaceEpsilonTextField, this, "surfEpsilon"),
+				new ControlManager(textFieldIntSensitivity, 0, 1000,
+						interactionsTextField, this, "maxIntersections"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						yStartTextField, this, "yStartBeamPos"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						yEndTextField, this, "yEndBeamPos"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						xSourcePlaneTextField, this, "xBeamSourceRefPlane"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						xTargetPlaneTextField, this, "xBeamRotationPlane"),
+				new ControlManager(textFieldDoubleSensitivity, 0, 1e10,
+						spaceBoxLimitTextField, this, "virtualSpaceSize"),
+				new ControlManager(textFieldDoubleSensitivity, -1e10, 1e10,
+						beamOffsetTextField, this, "beamAngle"),
+				new ControlManager(textFieldIntSensitivity, 0, 1000,
+						dispersionCountTextField, this, "dispersionBeams"),
+				new ControlManager(textFieldIntSensitivity, 0, 50000,
+						lineLimitTextField, this, "tableLineLimit"),
+				new ControlManager(inverseCheckBox, this, "inverse"),
+				new ControlManager(gridCheckBox, this, "showGrid"),
+				new ControlManager(antiAliasCheckBox, this, "antialias"),
+				new ControlManager(showControlsCheckBox, this, "showControls"),
+				new ControlManager(divergingBeamsCheckBox, this,
+						"divergingSource") };
+		// new ControlManager(rotateFromXCheckBox, this, "rotXZero"), };
+
+		for (ControlManager cm : array) {
+			programControlList.put(cm.getTag(), cm);
+		}
+	}
+
+	void setupColorButtons() {
+		ColorButton[] array = new ColorButton[] {
+				// The tag names must correspond to names of declared fields
+				// in the program values class
+				new ColorButton(this, "colorBaseline",
+						"X/Y Zero Baseline color"),
+				new ColorButton(this, "colorGrid", "Grid color"),
+				new ColorButton(this, "colorLensOutline",
+						"Lens body color : unselected"),
+				new ColorButton(this, "colorLensSelected",
+						"Lens body color : selected"),
+				new ColorButton(this, "colorHighBackground",
+						"High background color"),
+				new ColorButton(this, "colorLowBackground",
+						"Low background color"),
+				new ColorButton(this, "colorArrow", "Intersection arrow color"),
+				new ColorButton(this, "colorBeam", "Light Beam color"),
+				new ColorButton(this, "colorTerminator",
+						"Ray Termination color"),
+				new ColorButton(this, "colorLightSource",
+						"Light source bar color") };
+		// colorButtonList = new HashMap<>();
+		for (ColorButton cb : array) {
+			programControlList.put(cb.getTag(), cb);
+			colorToolBar.add(cb);
+		}
+	}
+
+	int currentTab() {
+		return tabbedPane.getSelectedIndex();
+	}
+
+	void resetButtonColors() {
+		for (ProgramControl cb : programControlList.values()) {
+			cb.reset();
+		}
+	}
+
+	void setSelectedComponent(OpticalComponent oc) {
+		selectedComponent = oc;
+		selectedComponent.writeObjectControls();
+		// move to lens design pane
+		tabbedPane.setSelectedIndex(Common.TAB_DESIGN);
+		processTabChange();
+		programValues.selectedComponent = getSelectedComponent();
+	}
+
+	void setSelectedComponent(int sel) {
+		if (sel >= 0 && sel < componentList.size()) {
+			OpticalComponent oc = componentList.get(sel);
+			setSelectedComponent(oc);	
+		}
+		else {
+			clearSelection();
+		}
+	}
+	
+	int getSelectedComponent() {
+		int sel = componentList.indexOf(selectedComponent);
+		return sel;
+	}
+
+	void clearSelection() {
+		selectedComponent = null;
+		programValues.selectedComponent = -1;
+		for (ControlManager cm : objectControlList.values()) {
+			cm.enable(false);
+		}
+		enableTextField(internalThicknessTextField,false);
+		enableTextField(centerThicknessTextField,false);
+	}
+	
+	void enableTextField(JTextField tf,boolean enabled) {
+		tf.setEnabled(enabled);
+		if(!enabled) {
+			tf.setText("(Select a lens)");
+		}
+	}
+
+	void selectNextObject() {
+		if (selectedComponent == null) {
+			if (componentList.size() > 0) {
+				setSelectedComponent(componentList.get(0));
+			} else {
+				Common.beep();
+			}
+		} else {
+			int n = componentList.size();
+			for (int i = 0; i < n; i++) {
+				if (componentList.get(i) == selectedComponent) {
+					i = (i + 1) % n;
+					setSelectedComponent(componentList.get(i));
+				}
+			}
+		}
+		updateGraphicDisplay();
+	}
+
+	void updateGraphicDisplay() {
+		gPaneDesign.updateDisplay();
+		gPaneConfigure.updateDisplay();
+	}
+
+	void enableComponentControls(boolean enabled) {
+		for (ControlManager cm : objectControlList.values()) {
+			cm.enable(enabled);
+		}
+		internalThicknessTextField.setEnabled(enabled);
+		centerThicknessTextField.setEnabled(enabled);
+		if (selectedComponent != null) {
+			int rc = rightCurvComboBox.getSelectedIndex();
+			int lc = leftCurvComboBox.getSelectedIndex();
+			boolean lhp = lc == Common.CURVATURE_PLANAR;
+			boolean rhp = rc == Common.CURVATURE_PLANAR;
+			leftSphereRadiusTextField.setEnabled(!lhp);
+			boolean state = enabled & !selectedComponent.values.symmetrical;
+			rightSphereRadiusTextField.setEnabled(state && !rhp);
+			rightCurvComboBox.setEnabled(state);
+			boolean rhs = rc == Common.CURVATURE_HYPERBOLIC;
+			boolean lhs = lc == Common.CURVATURE_HYPERBOLIC;
+			rightZTextField.setEnabled(state && rhs);
+			leftZTextField.setEnabled(lhs);
+		}
+	}
+
+	double getDouble(String s) {
+		return LocaleHandler.getDouble(s, LocaleHandler.localeDecimalSeparator);
+	}
+
+	void readProgramControls() {
+		for (ProgramControl cm : programControlList.values()) {
+			programValues.setOneValue(cm.getTag(), cm.getValue());
+		}
+		checkXSourcePlane();
+		setupSelectedComponent();
+	}
+
+	void writeProgramControls() {
+		for (ProgramControl cm : programControlList.values()) {
+			cm.setValue(programValues.getOneValue(cm.getTag()));
+		}
+		setupSelectedComponent();
+
+	}
+
+	void setupSelectedComponent() {
+		lensDesignControlPane.setVisible(programValues.showControls);
+		if (selectedComponent != null) {
+			selectedComponent.readObjectControls();
+		}
+		enableComponentControls(selectedComponent != null);
+		updateGraphicDisplay();
+	}
+
+	// required during startup to assure all elements
+	// are initialized with configuration file values
+	void writeElementControls() {
+		for (OpticalComponent e : componentList) {
+			e.writeObjectControls();
+		}
+	}
+
+	void checkXSourcePlane() {
+		double x = programValues.xBeamSourceRefPlane;
+		if (abs(x) > programValues.virtualSpaceSize) {
+			showNotifyMessage(
+					"The X source plane cannot lie outside the\nvirtual space box size -- adjusting value.",
+					"X source outside domain");
+			x = max(x, -programValues.virtualSpaceSize);
+			x = min(x, programValues.virtualSpaceSize);
+			programValues.xBeamSourceRefPlane = x;
+			writeProgramControls();
+		}
+	}
+
+	
+
+	void makeNewObjectPopup(boolean moveToRight, double x, double y,
+			int function) {
+		OpticalComponent oc = makeNewComponent(moveToRight, function);
+		Vector p = displayToSpace(x, y);
+		oc.values.xPos = p.x + programValues.xOffset;
+		oc.values.yPos = p.y + programValues.yOffset;
+		oc.reconfigure();
+		oc.writeObjectControls();
+	}
+
+	void makeNewLensPopup(double x, double y) {
+		makeNewObjectPopup(false, x, y, Common.OBJECT_REFRACTOR);
+	}
+
+	void makeNewMirrorPopup(double x, double y) {
+		makeNewObjectPopup(false, x, y, Common.OBJECT_REFLECTOR);
+	}
+
+	public void unSelectLens() {
+		undoPush();
+		clearSelection();
+		updateGraphicDisplay();
+	}
+
+	void deleteSelectedLens() {
+		if (selectedComponent != null) {
+			boolean delete = false;
+			if (programValues.askBeforeDeleting) {
+				boolean[] reply = showConfirmMessage("Okay to delete "
+						+ selectedComponent.values.name + "?", "Delete Lens",
+						"In future, delete without asking");
+				if (reply[0]) {
+					delete = true;
+				}
+				if (reply[1]) {
+					programValues.askBeforeDeleting = false;
+				}
+
+			} else {
+				delete = true;
+			}
+			if (delete) {
+				this.componentList.remove(selectedComponent);
+				clearSelection();
+				updateGraphicDisplay();
+			}
+		}
+	}
+
+	boolean[] showConfirmMessage(String message, String title, String extra) {
+		int reply = -1;
+		boolean extraReply = false;
+		if (extra.length() > 0) {
+			JCheckBox checkbox = new JCheckBox(extra);
+			Object[] params = { message, checkbox };
+			reply = JOptionPane.showConfirmDialog(frame, params, appName + ": "
+					+ title, JOptionPane.YES_NO_CANCEL_OPTION);
+			extraReply = checkbox.isSelected();
+		} else {
+			reply = JOptionPane.showConfirmDialog(frame, message, appName
+					+ ": " + title, JOptionPane.YES_NO_CANCEL_OPTION);
+		}
+		return new boolean[] { reply == JOptionPane.YES_OPTION, extraReply };
+	}
+
+	void showNotifyMessage(String message, String title) {
+		JOptionPane.showMessageDialog(frame, message, appName + ": " + title,
+				JOptionPane.INFORMATION_MESSAGE);
+	}
+
+	void showNotifyMessageFormatted(String message, String title) {
+		JTextArea ta = new JTextArea(message);
+		ta.setBackground(frame.getBackground());
+		Font f = new Font("Monospaced", Font.PLAIN, 11);
+		ta.setFont(f);
+		JOptionPane.showMessageDialog(frame, ta, appName + ": " + title,
+				JOptionPane.INFORMATION_MESSAGE);
+	}
+
+	OpticalComponent makeNewComponent(String data, boolean moveToRight,
+			int function) {
+		undoPush();
+		OpticalComponent oc = makeGenericComponent(data, function);
+		if (moveToRight && componentList.size() > 0) {
+			double ox = (selectedComponent != null) ? selectedComponent.values.xPos : 0;
+			for (OpticalComponent v : componentList) {
+				if (v.values.function != Common.OBJECT_ABSORBER) {
+					ox = max(ox, v.values.xPos);
+				}
+			}
+			oc.values.xPos = ox + 1;
+			oc.reconfigure();
+		}
+		componentList.add(oc);
+		selectedComponent = oc;
+		oc.snapToGrid();
+		updateGraphicDisplay();
+		return oc;
+	}
+
+	OpticalComponent makeNewComponent(boolean moveToRight, int function) {
+		return makeNewComponent(null, moveToRight, function);
+	}
+
+	OpticalComponent makeGenericComponent(String data, int function) {
+		return new OpticalComponent(this, data, function);
+	}
+
+	OpticalComponent makeGenericComponent(int function) {
+		return new OpticalComponent(this, function);
+	}
+
+	void setDefaults(boolean newLenses) {
+		ProgramValues pv = new ProgramValues();
+		String config = pv.getValues();
+		programValues.setValues(config);
+		xSize = -1;
+		ySize = -1;
+		xCenter = -1;
+		yCenter = -1;
+		resetButtonColors();
+		if (newLenses) {
+			componentList = new ArrayList<>();
+			makeDefaultObjects(true);
+		}
+		writeProgramControls();
+		resetUndoRedo();
+		clearSelection();
+	}
+
+	// create two generic default lenses
+	void makeDefaultObjects(boolean update) {
+		if (componentList.size() == 0) {
+			OpticalComponent oc = new OpticalComponent(this,
+					Common.OBJECT_REFRACTOR);
+			oc.values.xPos = 0;
+			oc.reconfigure();
+			componentList.add(oc);
+			oc = new OpticalComponent(this, Common.OBJECT_REFRACTOR);
+			oc.values.leftSphereRadius = -4;
+			oc.values.rightSphereRadius = -4;
+			oc.values.thickness = 1.4;
+			oc.values.xPos = 2;
+			oc.reconfigure();
+			componentList.add(oc);
+			oc = new OpticalComponent(this, Common.OBJECT_ABSORBER);
+			oc.values.xPos = 30;
+			oc.values.lensRadius = 10;
+			oc.values.name = "Terminal Plane";
+			oc.reconfigure();
+			componentList.add(oc);
+			oc.snapToGrid();
+			clearSelection();
+			if (update) {
+				updateGraphicDisplay();
+			}
+		}
+	}
+
+	protected String formatNum(double v) {
+		return (Double.isNaN(v)) ? "-" : LocaleHandler.formatDouble(v,
+				programValues.decimalPlaces);
+	}
+
+	void clipboardCopyImage() {
+		// use the same aspect ratio as the display
+		// but a fixed horizontal size defined in program values
+		double height = this.gPaneDesign.getHeight()
+				/ (double) this.gPaneDesign.getWidth();
+		int h = (int) (programValues.clipboardGraphicXSize * height);
+		this.gPaneDesign.rayTraceProcessCore(
+				programValues.clipboardGraphicXSize, h, true);
+		ImageTransferable imt = new ImageTransferable(image);
+		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+		clipboard.setContents(imt, null);
+	}
+
+	void clipboardCopyFullConfig() {
+		String config = this.initManager.getFullConfiguration(true);
+		setClipboardContents(config);
+	}
+
+	void clipboardCopyString(String s) {
+		setClipboardContents(s);
+	}
+
+	void clipboardPasteFullConfig() {
+		String data = getClipboardContents();
+		if (data != null) {
+
+			boolean[] reply = showConfirmMessage(
+					"Okay to read full configuration (erases all current settings)?",
+					"Read Full Configuration", "");
+			if (reply[0]) {
+				initManager.setFullConfiguration(data);
+				writeProgramControls();
+			}
+		}
+	}
+
+	void setClipboardContents(String s) {
+		StringSelection stringSelection = new StringSelection(s);
+		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+		clipboard.setContents(stringSelection, frame);
+	}
+
+	String getClipboardContents() {
+		String s = null;
+
+		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+		Transferable content = clipboard.getContents(this);
+		if (content != null) {
+			try {
+				s = (String) content.getTransferData(DataFlavor.stringFlavor);
+			} catch (Exception e) {
+				System.out.println("getClipboardContents2: " + e);
+			}
+		}
+
+		return s;
+	}
+
+	void clipboardCutLens() {
+		OpticalComponent lens;
+		if ((lens = clipboardCopyLens()) != null) {
+			undoPush();
+			this.componentList.remove(lens);
+			clearSelection();
+			updateGraphicDisplay();
+		}
+	}
+
+	OpticalComponent clipboardCopyLens() {
+		OpticalComponent lens = null;
+		if (selectedComponent != null) {
+			lens = selectedComponent;
+			setClipboardContents(lens.toString());
+		}
+		return lens;
+	}
+
+	void clipboardPasteObject(boolean toMouse) {
+		String s = getClipboardContents();
+		if (s != null) {
+			if (!decodeMultiLensString(s)) {
+				OpticalComponent newLens = new OpticalComponent(this, s,
+						Common.OBJECT_REFRACTOR);
+				if (newLens.valid) {
+					undoPush();
+					if (toMouse) {
+						// position lens at mouse cursor
+						Vector p = displayToSpace(popupMouseX, popupMouseY);
+						newLens.values.xPos = p.x + programValues.xOffset;
+						newLens.values.yPos = p.y + programValues.yOffset;
+					}
+					componentList.add(newLens);
+					newLens.snapToGrid();
+					// select this lens
+					setSelectedComponent(newLens);
+					updateGraphicDisplay();
+				}
+			}
+		}
+	}
+
+	protected boolean decodeMultiLensString(String data) {
+		boolean result = false;
+		String s = "(?s)component \\{\\s*(.*?)\\s*\\}";
+		Pattern pat = Pattern.compile(s);
+		Matcher m = pat.matcher(data);
+		while (m.find()) {
+			String v = m.group(1);
+			makeNewComponent(v, false, Common.OBJECT_REFRACTOR);
+			result = true;
+		}
+		return result;
+	}
+
+	void eraseReset() {
+		boolean[] reply = showConfirmMessage(
+				"Okay to reset program to default settings\n(erases all entries and setting changes)?",
+				"Reset to defaults", "");
+		if (reply[0]) {
+			setDefaults(true);
+		}
+	}
+
+	void resetUndoRedo() {
+		undoStack.clear();
+		redoStack.clear();
+		updateUndoRedoButtons();
+	}
+
+	void limitUndoRedoStackSize() {
+		while (undoStack.size() > undoRedoMaxStack) {
+			undoStack.remove(0);
+		}
+		while (redoStack.size() > undoRedoMaxStack) {
+			redoStack.remove(0);
+		}
+	}
+
+	void undoPush() {
+		if (undoFlag && redoFlag) {
+			String state = initManager.getFullConfiguration(false);
+			String current = (undoStack.size() > 0) ? undoStack.lastElement()
+					: "";
+			if (!current.equals(state)) {
+				undoStack.push(state);
+			}
+			updateUndoRedoButtons();
+			limitUndoRedoStackSize();
+		}
+	}
+
+	void redoPush() {
+		if (redoFlag) {
+			String state = initManager.getFullConfiguration(false);
+			String current = (redoStack.size() > 0) ? undoStack.lastElement()
+					: "";
+			if (!current.equals(state)) {
+				redoStack.push(state);
+			}
+			updateUndoRedoButtons();
+			limitUndoRedoStackSize();
+		}
+	}
+
+	void undoPop() {
+		if (undoStack.size() > 0 && undoFlag) {
+			undoFlag = false;
+			redoPush();
+			initManager.setFullConfiguration(undoStack.pop());
+			this.selectedComponent = null;
+			updateUndoRedoButtons();
+			undoFlag = true;
+			updateGraphicDisplay();
+			setupSelectedComponent();
+		} else {
+			Common.beep();
+		}
+	}
+
+	void redoPop() {
+		if (redoStack.size() > 0 && redoFlag) {
+			undoPush();
+			redoFlag = false;
+			initManager.setFullConfiguration(redoStack.pop());
+			this.selectedComponent = null;
+			updateUndoRedoButtons();
+			redoFlag = true;
+			updateGraphicDisplay();
+			setupSelectedComponent();
+		} else {
+			Common.beep();
+		}
+	}
+
+	void updateUndoRedoButtons() {
+		redoButton.setEnabled(redoStack.size() > 0);
+		undoButton.setEnabled(undoStack.size() > 0);
+	}
+
+	void setSelectedLens(OpticalComponent p) {
+		selectedComponent = p;
+		setupSelectedComponent();
+	}
+
+	protected void copyLineList(boolean html) {
+		String data = (html) ? lineAnalysis.makeHTMLTable(true) : lineAnalysis
+				.makeCSVTable();
+		setClipboardContents(data);
+	}
+
+	ComplexInt spaceToDisplay(double x, double y) {
+		int dx = (int) (((x - programValues.xOffset) * programValues.dispScale * ySize) + xCenter);
+		int dy = (int) (yCenter - ((y - programValues.yOffset)
+				* programValues.dispScale * ySize));
+		return new ComplexInt(dx, dy);
+	}
+
+	Vector displayToSpace(double dx, double dy) {
+		double x = ((dx - xCenter) / (programValues.dispScale * ySize));
+		double y = ((yCenter - dy) / (programValues.dispScale * ySize));
+		return new Vector(x, y);
+	}
+
+	Vector displayToSpaceOffset(Vector p) {
+		double x = ((p.x - xCenter) / (programValues.dispScale * ySize))
+				+ programValues.xOffset;
+		double y = ((yCenter - p.y) / (programValues.dispScale * ySize))
+				+ programValues.yOffset;
+		return new Vector(x, y);
+	}
+
+	void writeLog(String data) {
+		try {
+			String path = System.getProperty("user.home")
+					+ "/OPticalRayTracerDebugLog.txt";
+			FileWriter fw = new FileWriter(path, true);
+			fw.write(data + "\n");
+			fw.close();
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	protected void processTabChange() {
+		switch (currentTab()) {
+		case Common.TAB_TABLE:
+			if (dataTableDisplay != null) {
+				dataTableDisplay.requestFocus();
+			}
+			break;
+		case Common.TAB_CONFIGURE:
+			if (gPaneConfigure != null) {
+				gPaneConfigure.acquireFocus();
+			}
+			break;
+		case Common.TAB_DESIGN:
+			if (gPaneDesign != null) {
+				gPaneDesign.acquireFocus();
+			}
+		}
+	}
+
+	/**
+	 * Launch the application.
+	 */
+	public static void main(final String[] args) {
+		
+		EventQueue.invokeLater(new Runnable() {
+			public void run() {
+				try {
+					OpticalRayTracer window = new OpticalRayTracer(args);
+					window.frame.setVisible(true);
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		});
+	}
+
+	protected void exit() {
+		readProgramControls();
+		programValues.windowX = frame.getX();
+		programValues.windowY = frame.getY();
+		programValues.defaultWindowWidth = frame.getWidth();
+		programValues.defaultWindowHeight = frame.getHeight();
+		programValues.selectedTab = tabbedPane.getSelectedIndex();
+		this.helpPanel.onQuit();
+		initManager.writeConfig(true);
+		frame.setVisible(false);
+		frame.dispose();
+	}
+
+	/**
+	 * Initialize the contents of the frame.
+	 */
+	private void initialize() {
+		frame = new MyJFrame();
+		frame.addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosing(WindowEvent e) {
+				exit();
+			}
+		});
+		Dimension minSize = new Dimension(700, 500);
+		frame.setBounds(100, 100, minSize.width, minSize.height);
+		frame.setMinimumSize(new Dimension(900, 400));
+		frame.setPreferredSize(new Dimension(900, 600));
+		frame.setSize(minSize);
+		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+
+		tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
+		tabbedPane.addChangeListener(new ChangeListener() {
+			public void stateChanged(ChangeEvent e) {
+				processTabChange();
+			}
+		});
+		tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
+		frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
+
+		designPane = new JPanel();
+
+		designPane.setToolTipText("");
+		designPane.setBackground(SystemColor.window);
+		tabbedPane
+				.addTab("Design",
+						new ImageIcon(
+								OpticalRayTracer.class
+										.getResource("/opticalraytracer/icons/applications-graphics.png")),
+						designPane, "Design elements and optical layout");
+		designPane.setLayout(new BorderLayout(0, 0));
+
+		JPanel controlPane = new JPanel();
+		designPane.add(controlPane, BorderLayout.SOUTH);
+		controlPane.setLayout(new BorderLayout(0, 0));
+
+		JToolBar toolBar = new JToolBar(fullAppName + " Program Controls");
+		controlPane.add(toolBar, BorderLayout.NORTH);
+
+		JButton newLensButton = new JButton("");
+		newLensButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				makeNewComponent(true, Common.OBJECT_REFRACTOR);
+			}
+		});
+		newLensButton
+				.setToolTipText("Create new lens to right of existing objects");
+		newLensButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/document-new.png")));
+		toolBar.add(newLensButton);
+
+		JButton resetButton = new JButton("");
+		resetButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				eraseReset();
+			}
+		});
+
+		newMIrrorButton = new JButton("");
+		newMIrrorButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				makeNewComponent(true, Common.OBJECT_REFLECTOR);
+			}
+		});
+
+		newMIrrorButton
+				.setToolTipText("Create new reflector/absorber to right of existing objects");
+		newMIrrorButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/view-fullscreen.png")));
+		toolBar.add(newMIrrorButton);
+		resetButton
+				.setToolTipText("<html>Erase present work, reset to defaults<br/>(Asks for confirmation)");
+		resetButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/process-stop.png")));
+		toolBar.add(resetButton);
+
+		JButton copyImageButton = new JButton("");
+		copyImageButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				clipboardCopyImage();
+			}
+		});
+		copyImageButton
+				.setToolTipText("Copy workspace graphic image to clipboard");
+		copyImageButton
+				.setIcon(new ImageIcon(
+						OpticalRayTracer.class
+								.getResource("/opticalraytracer/icons/applications-multimedia.png")));
+		toolBar.add(copyImageButton);
+
+		JButton copyConfigurationButton = new JButton("");
+		copyConfigurationButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				clipboardCopyFullConfig();
+			}
+		});
+		copyConfigurationButton
+				.setToolTipText("Copy full configuration to clipboard");
+		copyConfigurationButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/edit-copy.png")));
+		toolBar.add(copyConfigurationButton);
+
+		JButton pasteConfigurationButton = new JButton("");
+		pasteConfigurationButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				clipboardPasteFullConfig();
+			}
+		});
+		pasteConfigurationButton
+				.setToolTipText("<html>Paste full configuration from clipboard<br/>(Asks for confirmation)");
+		pasteConfigurationButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/edit-paste.png")));
+		toolBar.add(pasteConfigurationButton);
+
+		undoButton = new JButton("");
+		undoButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				undoPop();
+			}
+		});
+		undoButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/edit-undo.png")));
+		undoButton.setToolTipText("Undo most recent action");
+		toolBar.add(undoButton);
+
+		redoButton = new JButton("");
+		redoButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				redoPop();
+			}
+		});
+		redoButton.setToolTipText("Redo most recent undone action");
+		redoButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/edit-redo.png")));
+		toolBar.add(redoButton);
+
+		button = new JButton("");
+		button.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				selectNextObject();
+			}
+		});
+
+		unselectButton = new JButton("");
+		unselectButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				unSelectLens();
+			}
+		});
+		unselectButton.setToolTipText("Clear present selection");
+		unselectButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/edit-clear.png")));
+		toolBar.add(unselectButton);
+		button.setToolTipText("Cycle through lens selections");
+		button.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/view-refresh.png")));
+		toolBar.add(button);
+
+		inverseCheckBox = new JCheckBox("Inverse");
+		inverseCheckBox.setAlignmentY(0.53f);
+		inverseCheckBox.setBorder(new CompoundBorder(new LineBorder(new Color(153, 153, 153)), new LineBorder(new Color(238, 238, 238), 8)));
+		inverseCheckBox.setBorderPainted(true);
+		inverseCheckBox.setToolTipText("Dark background");
+		toolBar.add(inverseCheckBox);
+
+		gridCheckBox = new JCheckBox("Grid");
+		gridCheckBox.setAlignmentY(0.53f);
+		gridCheckBox.setBorderPainted(true);
+		gridCheckBox.setBorder(new CompoundBorder(new LineBorder(new Color(153, 153, 153)), new LineBorder(new Color(238, 238, 238), 8)));
+		gridCheckBox.setSelected(true);
+		gridCheckBox.setToolTipText("Show grid lines");
+		toolBar.add(gridCheckBox);
+
+		antiAliasCheckBox = new JCheckBox("Antialias");
+		antiAliasCheckBox.setAlignmentY(0.53f);
+		antiAliasCheckBox.setBorderPainted(true);
+		antiAliasCheckBox.setBorder(new CompoundBorder(new LineBorder(new Color(153, 153, 153)), new LineBorder(new Color(238, 238, 238), 8)));
+		antiAliasCheckBox.setSelected(true);
+		antiAliasCheckBox.setToolTipText("Best appearance, slower drawing");
+		toolBar.add(antiAliasCheckBox);
+
+		showControlsCheckBox = new JCheckBox("Controls");
+		showControlsCheckBox.setAlignmentY(0.53f);
+		showControlsCheckBox.setBorderPainted(true);
+		showControlsCheckBox.setBorder(new CompoundBorder(new LineBorder(new Color(153, 153, 153)), new LineBorder(new Color(238, 238, 238), 8)));
+		showControlsCheckBox.setSelected(true);
+		showControlsCheckBox
+				.setToolTipText("Show/hide lens design control panel");
+		toolBar.add(showControlsCheckBox);
+
+		JButton quitButton = new JButton("");
+		quitButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				exit();
+			}
+		});
+		quitButton.setToolTipText("Quit OpticalRayTracer");
+		quitButton.setIcon(new ImageIcon(OpticalRayTracer.class
+				.getResource("/opticalraytracer/icons/application-exit.png")));
+		toolBar.add(quitButton);
+
+		lensDesignControlPane = new JPanel();
+		controlPane.add(lensDesignControlPane, BorderLayout.CENTER);
+		lensDesignControlPane.setLayout(new MigLayout("", "[399px][492px]", "[110px][35px]"));
+
+		JPanel westSubPanel = new JPanel();
+		westSubPanel.setToolTipText("Optical element controls");
+		westSubPanel.setBorder(new LineBorder(new Color(0, 0, 0)));
+		lensDesignControlPane.add(westSubPanel, "cell 0 0,alignx left,aligny top");
+		westSubPanel.setLayout(new MigLayout("", "[111px][grow][][grow]", "[23px][][][][]"));
+
+		lblName = new JLabel("Name");
+		westSubPanel.add(lblName, "cell 0 0,alignx left");
+
+		nameTextField = new JTextField();
+		nameTextField.setToolTipText("Distinctive names help organize complex optical configurations");
+
+		westSubPanel.add(nameTextField, "cell 1 0 3 1,growx");
+		nameTextField.setColumns(10);
+
+		JLabel lblLensRadius = new JLabel("Radius");
+		westSubPanel.add(lblLensRadius, "cell 0 1");
+
+		lensRadiusTextField = new JTextField();
+		lensRadiusTextField
+				.setToolTipText("<html>Center-to-edge radius<br/>(red if value conflict exists)");
+		lensRadiusTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		westSubPanel.add(lensRadiusTextField, "cell 1 1");
+		lensRadiusTextField.setColumns(10);
+
+		JLabel lblX = new JLabel("X");
+		westSubPanel.add(lblX, "cell 2 2,alignx center");
+		
+				JLabel lblPosition = new JLabel("Position");
+				westSubPanel.add(lblPosition, "cell 3 1,alignx center");
+
+		JLabel lblThickness = new JLabel("Edge Thickness");
+		lblThickness.setHorizontalAlignment(SwingConstants.LEFT);
+		westSubPanel.add(lblThickness, "cell 0 2,alignx left");
+
+		thicknessTextField = new JTextField();
+		thicknessTextField
+				.setToolTipText("<html>Element edge thickness<br/>(red if value conflict exists)");
+		thicknessTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		westSubPanel.add(thicknessTextField, "cell 1 2");
+		thicknessTextField.setColumns(10);
+
+		JLabel lblY = new JLabel("Y");
+		westSubPanel.add(lblY, "cell 2 3,alignx center");
+		
+				xPosTextField = new JTextField();
+				westSubPanel.add(xPosTextField, "cell 3 2");
+				xPosTextField.setToolTipText("Lens X (horizontal) coordinate");
+				xPosTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+				xPosTextField.setColumns(10);
+
+		lblEffectiveThickness = new JLabel("Effective Thickness");
+		lblEffectiveThickness.setHorizontalAlignment(SwingConstants.LEFT);
+		westSubPanel.add(lblEffectiveThickness, "cell 0 3,alignx left");
+
+		internalThicknessTextField = new JTextField();
+		internalThicknessTextField.setEditable(false);
+		internalThicknessTextField
+				.setToolTipText("<html>Element edge thickness after crossover prevention<br/>(red if value conflict exists)");
+		internalThicknessTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		internalThicknessTextField.setColumns(10);
+		westSubPanel.add(internalThicknessTextField, "cell 1 3,alignx right");
+		
+				yPosTextField = new JTextField();
+				westSubPanel.add(yPosTextField, "cell 3 3");
+				yPosTextField.setToolTipText("Lens Y (vertical) coordinate");
+				yPosTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+				yPosTextField.setColumns(10);
+		
+		lblCenterThickness = new JLabel("Center Thickness");
+		lblCenterThickness.setHorizontalAlignment(SwingConstants.LEFT);
+		westSubPanel.add(lblCenterThickness, "cell 0 4,alignx left");
+		
+		centerThicknessTextField = new JTextField();
+		centerThicknessTextField.setToolTipText("Element center thickness (computed)");
+		centerThicknessTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		centerThicknessTextField.setEditable(false);
+		centerThicknessTextField.setColumns(10);
+		westSubPanel.add(centerThicknessTextField, "cell 1 4,growx");
+
+		JPanel lensDesignControlBox = new JPanel();
+		lensDesignControlBox.setToolTipText("Optical surface controls");
+		lensDesignControlBox.setBorder(new LineBorder(new Color(0, 0, 0)));
+		lensDesignControlPane.add(lensDesignControlBox, "cell 1 0,grow");
+		lensDesignControlBox.setLayout(new MigLayout("", "[][][][][][][]", "[][][][]"));
+
+		symmetricalCheckBox = new JCheckBox("Sym");
+		lensDesignControlBox.add(symmetricalCheckBox, "cell 0 0");
+		symmetricalCheckBox
+				.setToolTipText("Make lens symmetrical (right and left the same)");
+
+		JLabel lblSphereRadius = new JLabel("Sphere Radius");
+		lblSphereRadius.setHorizontalAlignment(SwingConstants.LEFT);
+		lensDesignControlBox.add(lblSphereRadius, "cell 1 0,alignx center");
+
+		JLabel lblSelectCurvature = new JLabel("Curvature Class");
+		lensDesignControlBox.add(lblSelectCurvature,
+				"cell 2 0,alignx center");
+		
+		JLabel lblCurvatureFactor = new JLabel("Hyperbolic Factor");
+		lblCurvatureFactor.setHorizontalAlignment(SwingConstants.CENTER);
+		lensDesignControlBox.add(lblCurvatureFactor, "cell 3 0,alignx center");
+
+		JLabel lblLeft = new JLabel("Left");
+		lensDesignControlBox.add(lblLeft, "cell 0 1,alignx left");
+
+		leftSphereRadiusTextField = new JTextField();
+		leftSphereRadiusTextField
+				.setToolTipText("<html>Radius of left sphere that constructs this lens<br/>(red if invalid value)");
+		leftSphereRadiusTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lensDesignControlBox.add(leftSphereRadiusTextField, "cell 1 1");
+		leftSphereRadiusTextField.setColumns(10);
+		
+		leftCurvComboBox = new JComboBox<String>();
+		leftCurvComboBox.setToolTipText("Left surface curvature class");
+		
+		
+		
+		lensDesignControlBox.add(leftCurvComboBox, "cell 2 1,alignx center");
+		
+		leftZTextField = new JTextField();
+		leftZTextField.setToolTipText("Left hyperbolic curvature factor");
+		leftZTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lensDesignControlBox.add(leftZTextField, "cell 3 1");
+		leftZTextField.setColumns(10);
+
+		JLabel lblRight = new JLabel("Right");
+		lensDesignControlBox.add(lblRight, "cell 0 2,alignx left");
+
+		rightSphereRadiusTextField = new JTextField();
+		rightSphereRadiusTextField
+				.setToolTipText("<html>Radius of right sphere that constructs this lens<br/>(red if invalid value)");
+		rightSphereRadiusTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lensDesignControlBox.add(rightSphereRadiusTextField, "cell 1 2");
+		rightSphereRadiusTextField.setColumns(10);
+		
+		rightCurvComboBox = new JComboBox<String>();
+		rightCurvComboBox.setToolTipText("Right surface curvature class");
+		lensDesignControlBox.add(rightCurvComboBox, "cell 2 2,alignx center");
+		
+		rightZTextField = new JTextField();
+		rightZTextField.setToolTipText("Right hyperbolic curvature factor");
+		rightZTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lensDesignControlBox.add(rightZTextField, "cell 3 2");
+		rightZTextField.setColumns(10);
+		
+				activeCheckBox = new JCheckBox("Active");
+				lensDesignControlBox.add(activeCheckBox, "cell 0 3");
+				activeCheckBox
+						.setToolTipText("Include this component in the optical calculation");
+				
+						refractRadioButton = new JRadioButton("Refract");
+						lensDesignControlBox.add(refractRadioButton, "cell 1 3");
+						refractRadioButton.setToolTipText("Refract all intersecting rays");
+						refractRadioButton.setName("");
+						typeButtonGroup.add(refractRadioButton);
+						
+								reflectRadioButton = new JRadioButton("Reflect");
+								lensDesignControlBox.add(reflectRadioButton, "cell 2 3");
+								reflectRadioButton.setToolTipText("Reflect all intersecting rays");
+								reflectRadioButton.setName("");
+								typeButtonGroup.add(reflectRadioButton);
+								
+										absorbRadioButton = new JRadioButton("Absorb");
+										lensDesignControlBox.add(absorbRadioButton, "cell 3 3");
+										absorbRadioButton.setToolTipText("Absorb all intersecting rays");
+										absorbRadioButton.setName("");
+										typeButtonGroup.add(absorbRadioButton);
+
+		JPanel lensOptionsControlBox = new JPanel();
+		lensOptionsControlBox.setToolTipText("Optical behavior/angle controls");
+		lensOptionsControlBox.setBorder(new LineBorder(new Color(0, 0, 0)));
+		lensDesignControlPane.add(lensOptionsControlBox, "cell 0 1 2 1,growx,aligny top");
+		lensOptionsControlBox.setLayout(new MigLayout("", "[][][][][][][][][]",
+				"[]"));
+
+		JLabel lblIor = new JLabel("IOR");
+		lensOptionsControlBox.add(lblIor, "cell 0 0");
+
+		iorTextField = new JTextField();
+		iorTextField.setToolTipText("Index of refraction");
+		iorTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lensOptionsControlBox.add(iorTextField, "cell 1 0");
+		iorTextField.setColumns(10);
+
+		JLabel lblDispersion = new JLabel("Abbe number");
+		lensOptionsControlBox.add(lblDispersion, "cell 2 0");
+
+		dispersionTextField = new JTextField();
+		dispersionTextField
+				.setToolTipText("Wavelength-dependent property of some media");
+		dispersionTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lensOptionsControlBox.add(dispersionTextField, "cell 3 0");
+		dispersionTextField.setColumns(10);
+
+		JLabel lblAngle = new JLabel("Angle");
+		lensOptionsControlBox.add(lblAngle, "cell 4 0");
+
+		angleTextField = new JTextField();
+		angleTextField.setToolTipText("Lens rotation angle");
+		angleTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		lensOptionsControlBox.add(angleTextField, "cell 5 0");
+		angleTextField.setColumns(10);
+
+		configurePane = new JPanel();
+		configurePane.setFocusTraversalKeysEnabled(false);
+		tabbedPane
+				.addTab("Configure",
+						new ImageIcon(
+								OpticalRayTracer.class
+										.getResource("/opticalraytracer/icons/applications-accessories.png")),
+						configurePane, "Set global options");
+		tabbedPane.setDisplayedMnemonicIndexAt(1, 1);
+		configurePane.setLayout(new BorderLayout(0, 0));
+
+		JPanel controlBox = new JPanel();
+		configurePane.add(controlBox, BorderLayout.SOUTH);
+		controlBox.setLayout(new BorderLayout(0, 0));
+
+		colorToolBar = new JToolBar(fullAppName + " Program Colors");
+		controlBox.add(colorToolBar, BorderLayout.NORTH);
+
+		JPanel programControlPane = new JPanel();
+		controlBox.add(programControlPane, BorderLayout.CENTER);
+		programControlPane.setLayout(new MigLayout("",
+				"[][grow][][grow][][grow][][]", "[][][][][]"));
+
+		JLabel lblIntersectionDotSize = new JLabel("Insersection arrow size");
+		programControlPane.add(lblIntersectionDotSize,
+				"cell 0 0,alignx trailing");
+
+		arrowSizeTextField = new JTextField();
+		arrowSizeTextField.setToolTipText("These mark each beam interaction");
+		arrowSizeTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(arrowSizeTextField, "cell 1 0");
+		arrowSizeTextField.setColumns(10);
+
+		JLabel lblSnapValue = new JLabel("Snap-to-grid value");
+		programControlPane.add(lblSnapValue, "cell 2 0,alignx trailing");
+
+		snapValueTextField = new JTextField();
+		snapValueTextField
+				.setToolTipText("A nonzero value causes lenses to align themselves to the grid");
+		snapValueTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(snapValueTextField, "cell 3 0");
+		snapValueTextField.setColumns(10);
+
+		JLabel lblBeamWidth = new JLabel("Beam width");
+		programControlPane.add(lblBeamWidth, "cell 4 0,alignx trailing");
+
+		beamWidthTextField = new JTextField();
+		beamWidthTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(beamWidthTextField, "cell 5 0");
+		beamWidthTextField.setColumns(10);
+
+		JLabel lblNewLabel = new JLabel("Light beam Count");
+		programControlPane.add(lblNewLabel, "cell 0 1,alignx trailing");
+
+		beamCountTextField = new JTextField();
+		beamCountTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(beamCountTextField, "cell 1 1");
+		beamCountTextField.setColumns(10);
+
+		JLabel lblSourceYStart = new JLabel("Source Y start");
+		programControlPane.add(lblSourceYStart, "cell 2 1,alignx trailing");
+
+		yStartTextField = new JTextField();
+		yStartTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(yStartTextField, "cell 3 1");
+		yStartTextField.setColumns(10);
+
+		JLabel lblSourceYEnd = new JLabel("Source Y end");
+		programControlPane.add(lblSourceYEnd, "cell 4 1,alignx trailing");
+
+		yEndTextField = new JTextField();
+		yEndTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(yEndTextField, "cell 5 1");
+		yEndTextField.setColumns(10);
+
+		JLabel lblInteractionCount = new JLabel("Maximum interactions");
+		programControlPane.add(lblInteractionCount, "cell 0 2,alignx trailing");
+
+		interactionsTextField = new JTextField();
+		interactionsTextField.setToolTipText("Number of interactions per beam");
+		interactionsTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(interactionsTextField, "cell 1 2");
+		interactionsTextField.setColumns(10);
+
+		JLabel lblXSourcePlane = new JLabel("X source plane");
+		programControlPane.add(lblXSourcePlane, "cell 2 2,alignx trailing");
+
+		xSourcePlaneTextField = new JTextField();
+		xSourcePlaneTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(xSourcePlaneTextField, "cell 3 2");
+		xSourcePlaneTextField.setColumns(10);
+
+		JLabel lblXTargetPlane = new JLabel("X beam rotation plane");
+		programControlPane.add(lblXTargetPlane, "cell 4 2,alignx trailing");
+
+		xTargetPlaneTextField = new JTextField();
+		xTargetPlaneTextField
+				.setToolTipText("The vertical plane around which the light beams rotate");
+		xTargetPlaneTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(xTargetPlaneTextField, "cell 5 2");
+		xTargetPlaneTextField.setColumns(10);
+
+		JLabel lblNewLabel_1 = new JLabel("Beam offset angle");
+		programControlPane.add(lblNewLabel_1, "cell 0 3,alignx trailing");
+
+		beamOffsetTextField = new JTextField();
+		beamOffsetTextField
+				.setToolTipText("Allows beams to pass at an angle to the horizonal");
+		beamOffsetTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(beamOffsetTextField, "cell 1 3");
+		beamOffsetTextField.setColumns(10);
+
+		JLabel lblDispersionBeamCount = new JLabel("Dispersion beam count");
+		programControlPane.add(lblDispersionBeamCount,
+				"cell 2 3,alignx trailing");
+
+		dispersionCountTextField = new JTextField();
+		dispersionCountTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(dispersionCountTextField, "cell 3 3");
+		dispersionCountTextField.setColumns(10);
+
+		lblSurfaceEpsilon = new JLabel("Interlens Epsilon");
+		programControlPane.add(lblSurfaceEpsilon, "cell 4 3,alignx trailing");
+
+		interLensEpsilonTextField = new JTextField();
+		interLensEpsilonTextField
+				.setToolTipText("This value tells the ray tracer how to distinguish between lens surfaces");
+		interLensEpsilonTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		interLensEpsilonTextField.setColumns(10);
+		programControlPane.add(interLensEpsilonTextField, "cell 5 3");
+
+		lblLensEpsilon = new JLabel("Surface Epsilon");
+		programControlPane.add(lblLensEpsilon, "cell 0 4,alignx trailing");
+
+		surfaceEpsilonTextField = new JTextField();
+		surfaceEpsilonTextField
+				.setToolTipText("<html>This value represents an acceptance boundary around<br/>\n objects that allows them to be recognized by the ray tracer");
+		surfaceEpsilonTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		programControlPane.add(surfaceEpsilonTextField, "cell 1 4");
+		surfaceEpsilonTextField.setColumns(10);
+
+		lblSpaceBoxLimit = new JLabel("Virtual space box size");
+		programControlPane.add(lblSpaceBoxLimit, "cell 2 4,alignx trailing");
+
+		spaceBoxLimitTextField = new JTextField();
+		spaceBoxLimitTextField
+				.setToolTipText("The space box size (±) in the X and Y dimensions");
+		spaceBoxLimitTextField.setHorizontalAlignment(SwingConstants.RIGHT);
+		spaceBoxLimitTextField.setColumns(10);
+		programControlPane.add(spaceBoxLimitTextField, "cell 3 4");
+
+		divergingBeamsCheckBox = new JCheckBox("Diverging beams");
+		programControlPane.add(divergingBeamsCheckBox, "cell 4 4 2 1");
+
+		tablePane = new JPanel();
+		tablePane.setBackground(Color.WHITE);
+		tabbedPane
+				.addTab("Table",
+						new ImageIcon(
+								OpticalRayTracer.class
+										.getResource("/opticalraytracer/icons/x-office-spreadsheet.png")),
+						tablePane, "Show a table of the current traced rays");
+		tablePane.setLayout(new BorderLayout(0, 0));
+		statusBar = new JPanel();
+		statusBar.setBorder(new EmptyBorder(2, 4, 2, 0));
+		statusBar.setToolTipText("Program status and cursor position");
+		frame.getContentPane().add(statusBar, BorderLayout.SOUTH);
+		statusBar.setLayout(new BorderLayout(0, 0));
+
+		statusLabel = new JLabel("Program Status");
+		statusLabel.setFont(new Font("Courier", Font.BOLD, 12));
+		statusLabel.setHorizontalAlignment(SwingConstants.LEFT);
+		statusBar.add(statusLabel);
+	}
 }
diff --git a/src/opticalraytracer/OpticalRayTracerApp.form b/src/opticalraytracer/OpticalRayTracerApp.form
deleted file mode 100644
index 5b39167..0000000
--- a/src/opticalraytracer/OpticalRayTracerApp.form
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" ?>
-
-<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
-  <Properties>
-    <Property name="defaultCloseOperation" type="int" value="0"/>
-  </Properties>
-  <SyntheticProperties>
-    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
-  </SyntheticProperties>
-  <Events>
-    <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosing"/>
-  </Events>
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-  </AuxValues>
-
-  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-</Form>
diff --git a/src/opticalraytracer/OpticalRayTracerApp.java b/src/opticalraytracer/OpticalRayTracerApp.java
deleted file mode 100644
index 4f82662..0000000
--- a/src/opticalraytracer/OpticalRayTracerApp.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-/*
- * OpticalRayTracer.java
- *
- * Created on Feb 26, 2011, 10:32:39 PM
- */
-
-package opticalraytracer;
-
-import javax.swing.*;
-import javax.swing.UIManager.*;
-import java.awt.*;
-import java.util.Date.*;
-import java.net.*;
-
-/**
- *
- * @author lutusp
- */
-public class OpticalRayTracerApp extends javax.swing.JFrame {
-
-    OpticalRayTracer mainPanel;
-
-    /** Creates new form OpticalRayTracer */
-    public OpticalRayTracerApp(String[] args) {
-        initComponents();
-        mainPanel = new OpticalRayTracer(this,null,args);
-        setTitle(mainPanel.programName);
-        String icon_path = "icons/" + mainPanel.appName + ".png";
-        setIconImage(new ImageIcon(getClass().getResource(icon_path)).getImage());
-        GridBagConstraints gridBagConstraints = new GridBagConstraints();
-        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
-        gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.weighty = 1.0;
-        getContentPane().add(mainPanel, gridBagConstraints);
-    }
-
-    /** This method is called from within the constructor to
-     * initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is
-     * always regenerated by the Form Editor.
-     */
-    @SuppressWarnings("unchecked")
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-
-        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
-        addWindowListener(new java.awt.event.WindowAdapter() {
-            public void windowClosing(java.awt.event.WindowEvent evt) {
-                formWindowClosing(evt);
-            }
-        });
-        getContentPane().setLayout(new java.awt.GridBagLayout());
-
-        pack();
-    }// </editor-fold>//GEN-END:initComponents
-
-    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
-        mainPanel.close();
-    }//GEN-LAST:event_formWindowClosing
-
-    /**
-    * @param args the command line arguments
-    */
-    public static void main(final String args[]) {
-        try {
-            // Set to use system-specific L&F
-            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-        } catch (Exception e) {
-            // handle exception
-        }
-        java.awt.EventQueue.invokeLater(new Runnable() {
-            public void run() {
-                new OpticalRayTracerApp(args).setVisible(true);
-            }
-        });
-    }
-
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    // End of variables declaration//GEN-END:variables
-
-}
diff --git a/src/opticalraytracer/OpticalRayTracerApplet.form b/src/opticalraytracer/OpticalRayTracerApplet.form
deleted file mode 100644
index 763d346..0000000
--- a/src/opticalraytracer/OpticalRayTracerApplet.form
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" ?>
-
-<Form version="1.2" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JAppletFormInfo">
-  <AuxValues>
-    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
-    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
-    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
-    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
-    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,2,12,0,0,2,89"/>
-  </AuxValues>
-
-  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-</Form>
diff --git a/src/opticalraytracer/OpticalRayTracerApplet.java b/src/opticalraytracer/OpticalRayTracerApplet.java
deleted file mode 100644
index ab15684..0000000
--- a/src/opticalraytracer/OpticalRayTracerApplet.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
-/*
- * TankCalcApplet.java
- *
- * Created on Feb 9, 2009, 5:09:12 PM
- */
-package opticalraytracer;
-
-import javax.swing.*;
-import javax.swing.UIManager.*;
-import java.util.Date.*;
-
-import netscape.javascript.*;
-import java.text.Format.*;
-import java.text.DateFormat.*;
-
-/**
- *
- * @author lutusp
- */
-public class OpticalRayTracerApplet extends javax.swing.JApplet {
-
-    OpticalRayTracer mainPanel = null;
-    Thread cookie_thread;
-    String oldCookie = null;
-    JApplet applet;
-
-    /** Initializes the applet TankCalcApplet */
-    @Override
-    public void init() {
-        mainPanel = null;
-        applet = this;
-        try {
-            // Set to use system-specific L&F
-            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-        } catch (Exception e) {
-            // handle exception
-        }
-        // cookie update thread
-        try {
-            cookie_thread = new Thread() {
-
-                @Override
-                public void run() {
-                    while (true) {
-                        try {
-                            Thread.sleep(2000);
-                        } catch (Exception e) {
-                        }
-                        writeCookieTest();
-                    }
-                }
-            };
-            cookie_thread.start();
-
-            java.awt.EventQueue.invokeAndWait(new Runnable() {
-
-                public void run() {
-                    initComponents();
-                    mainPanel = new OpticalRayTracer(null, applet, null);
-                    setContentPane(mainPanel);
-                    SwingUtilities.invokeLater(
-                            new Runnable() {
-
-                                public void run() {
-                                    String config = readCookie();
-                                    mainPanel.setConfig(config);
-                                }
-                            });
-                }
-            });
-        } catch (Exception e) {
-            System.err.println("applet init: " + e);
-            e.printStackTrace();
-        }
-    }
-
-    void writeCookie(String data) {
-        if (data != null) {
-            data = StringEscapeUnescape.stringToHex(data);
-            try {
-                if (data != null) {
-                    java.util.Calendar c = java.util.Calendar.getInstance();
-                    c.add(java.util.Calendar.MONTH, 1);
-                    String expires = "; expires=" + c.getTime().toString();
-                    JSObject browser = JSObject.getWindow(this);
-                    if (browser != null) {
-                        JSObject document = (JSObject) browser.getMember("document");
-                        if (document != null) {
-                            String cookie = data + expires;
-                            document.setMember("cookie", cookie);
-                        }
-                    }
-                }
-            } catch (Exception e) {
-                System.err.println("writeCookie failed: " + e);
-            }
-        }
-    }
-
-    public void writeCookieTest() {
-        if (mainPanel != null) {
-            // only write a cookie if the entries have changed
-            String data = mainPanel.getConfig();
-            if (data != null && oldCookie != null && !data.equals(oldCookie)) {
-                writeCookie(data);
-            }
-            oldCookie = data;
-        }
-    }
-
-    public String readCookie() {
-        String result = null;
-        try {
-            JSObject browser = (JSObject) JSObject.getWindow(this);
-            JSObject document = (JSObject) browser.getMember("document");
-            //alert("readCookie myDocument = " + document);
-            if (document != null) {
-                String myCookie = (String) document.getMember("cookie");
-                if (myCookie != null && myCookie.length() > 0) {
-                    result = myCookie;
-                    result = result.replaceFirst("(.*?);.*", "$1");
-                }
-            }
-        } catch (Exception e) {
-            System.err.println("readCookie exception: " + e);
-        }
-        if (result != null) {
-            result = StringEscapeUnescape.hexToString(result);
-        }
-        return result;
-    }
-
-    /** This method is called from within the init() method to
-     * initialize the form.
-     * WARNING: Do NOT modify this code. The content of this method is
-     * always regenerated by the Form Editor.
-     */
-    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
-    private void initComponents() {
-
-        getContentPane().setLayout(new java.awt.GridBagLayout());
-    }// </editor-fold>//GEN-END:initComponents
-    // Variables declaration - do not modify//GEN-BEGIN:variables
-    // End of variables declaration//GEN-END:variables
-}
diff --git a/src/opticalraytracer/ProgramControl.java b/src/opticalraytracer/ProgramControl.java
new file mode 100644
index 0000000..52fb0cd
--- /dev/null
+++ b/src/opticalraytracer/ProgramControl.java
@@ -0,0 +1,8 @@
+package opticalraytracer;
+
+public interface ProgramControl {
+	String getValue();
+	void setValue(String s);
+	String getTag();
+	void reset();
+}
diff --git a/src/opticalraytracer/Box.java b/src/opticalraytracer/ProgramValues.java
similarity index 50%
rename from src/opticalraytracer/Box.java
rename to src/opticalraytracer/ProgramValues.java
index e25013a..d90d1c0 100644
--- a/src/opticalraytracer/Box.java
+++ b/src/opticalraytracer/ProgramValues.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,32 +17,53 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-/**
- *
- * @author lutusp
- */
-public class Box {
-
-    double left = 0, right = 0, bottom = 0, top = 0;
 
-    public Box(double l, double r, double b, double t) {
-        this.left = l;
-        this.right = r;
-        this.bottom = b;
-        this.top = t;
-    }
-
-    public Box() {
-    }
+package opticalraytracer;
 
-    public boolean inside(double x,double y) {
-        boolean inside = (x >= left && x < right && y >= bottom && y < top);
-        return inside;
-    }
+final public class ProgramValues extends ValueManager {
+	
+	int decimalPlaces = 4;
+	int windowX = 100;
+	int windowY = 100;
+	int defaultWindowWidth = 900;
+	int defaultWindowHeight = 600;
+	boolean inverse = false;
+	boolean showGrid = true;
+	boolean antialias = true;
+	boolean showControls = true;
+	int colorLensOutline =  0x1080a0ff;
+	int colorLensSelected = 0x1000c000;
+	int colorBaseline = 0x004000;
+	int colorGrid = 0x40c0c0c0;
+	int colorArrow = 0x800000ff;
+	int colorBeam = 0xc00000;
+	int colorHighBackground = 0xffffff;
+	int colorLowBackground = 0x000000;
+	int colorLightSource = 0x0000ff;
+	int colorTerminator = 0x000000;
+	int selectedTab = 0;
+	int selectedComponent = 0;
+	
+	int beamWidth = 1;
+	double xOffset = 0;
+	double yOffset = 0;
+	double dispScale = 3e-2;
+	double snapValue = .5;
+	double intersectionArrowSize = .05;
+    double yStartBeamPos = -1.8;
+    double yEndBeamPos = 1.8;
+    int dispersionBeams = 0;
+    int beamCount = 4;
+    int maxIntersections = 64;
+    double xBeamSourceRefPlane = -30.0;
+    double xBeamRotationPlane = 0;
+    double virtualSpaceSize = 1e2;
+    double beamAngle = 0;
+    double interLensEpsilon = 1e-6;
+    double surfEpsilon = 5e-4;
+    boolean divergingSource = false;
+    boolean askBeforeDeleting = true;
+    int clipboardGraphicXSize = 1280;
+    int helpScrollPos = 0;
+    int tableLineLimit = 500;
 }
diff --git a/src/opticalraytracer/RayLensIntersection.java b/src/opticalraytracer/RayLensIntersection.java
index b7dbc23..8e2edb7 100644
--- a/src/opticalraytracer/RayLensIntersection.java
+++ b/src/opticalraytracer/RayLensIntersection.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,29 +17,43 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
+
 package opticalraytracer;
 
-/**
- *
- * @author lutusp
- */
-public class RayLensIntersection {
 
-    int profile;
-    double x;
-    double y;
-    Lens lens;
+final public class RayLensIntersection {
+
+    int function;
+    boolean leftSide;
+    Vector a,b,p;
+    double wavelength;
+    double dot;
+    double m;
+    OpticalComponent lens = null;
 
-    public RayLensIntersection(int profile, double x, double y, Lens lens) {
-        this.profile = profile;
-        this.x = x;
-        this.y = y;
+    public RayLensIntersection(Vector a, Vector b, double wavelength, Vector p, boolean leftSide, OpticalComponent lens) {
+    	this.a = new Vector(a);
+    	this.b = new Vector(b);
+    	this.p = new Vector(p);
+    	this.wavelength = wavelength;
+        this.leftSide = leftSide;
         this.lens = lens;
+        this.function = lens.values.function;
+        
+        double cx = b.x - a.x;
+		double cy = b.y - a.y;
+		double dx = p.x - a.x;
+		double dy = p.y - a.y;
+		// current direction by dot product
+		dot = dx * cx  + dy * cy;
+		// magnitude
+		m = dx * dx + dy * dy;
+        
     }
     public RayLensIntersection() {
     }
+    
+    public String toString() {
+    	return String.format("ax = %f, ay = %f, bx = %f, by = %f, x = %f, y = %f, dot = %f, m = %f",a.x,a.y,b.x,b.y,p.x,p.y,dot,m);
+    }
 }
diff --git a/src/opticalraytracer/RayTraceComputer.java b/src/opticalraytracer/RayTraceComputer.java
index 6db3e41..91dc2a9 100644
--- a/src/opticalraytracer/RayTraceComputer.java
+++ b/src/opticalraytracer/RayTraceComputer.java
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
+ *   Copyright (C) 2014 by Paul Lutus                                      *
  *   lutusp at arachnoid.com                                                  *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,525 +17,452 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-import java.awt.*;
-import java.util.*;
-
-/**
- *
- * @author lutusp
- */
-final public class RayTraceComputer {
-
-    OpticalRayTracer parent;
-    final double toRadians = Math.PI / 180.0;
-    final double toDegrees = 180.0 / Math.PI;
-    // sodium yellow wavelength nm
-    final double dispersionPivot = 589.3;
-    final double dispersionFactor = 500000;
-    final double redWvlNm = 650;
-    final double vioWvlNm = 400;
-    final double epsilon = .001;
-    final ColorPos colors[] = {
-        new ColorPos(1.0, 0.0, 0.0, 650.0), // red
-        new ColorPos(1.0, 1.0, 0.0, 570.0), // yellow
-        new ColorPos(0.0, 1.0, 0.0, 510.0), // green
-        new ColorPos(0.0, 0.0, 1.0, 475.0), // blue
-        new ColorPos(.5, 0.0, 1.0, 445.0), // indigo
-        new ColorPos(.5, 0.0, .5, 400.0), // violet
-        new ColorPos(.5, 0.0, .5, 300.0) // terminator
-    };
-    MutableInt ipx = new MutableInt();
-    MutableInt ipy = new MutableInt();
-    MutableDouble dx1 = new MutableDouble();
-    MutableDouble dx2 = new MutableDouble();
-    MutableDouble dy1 = new MutableDouble();
-    MutableDouble dy2 = new MutableDouble();
-    MutableDouble dx = new MutableDouble();
-    MutableDouble dy = new MutableDouble();
-    MutableInt ix = new MutableInt();
-    MutableInt iy = new MutableInt();
-    MutableDouble xa = new MutableDouble();
-    MutableDouble ya = new MutableDouble();
-    MutableDouble xb = new MutableDouble();
-    MutableDouble yb = new MutableDouble();
-
-    public RayTraceComputer(OpticalRayTracer p) {
-        parent = p;
-    }
-
-    void drawLenses(Graphics g) {
-        Iterator<Lens> it = parent.sv_lensList.iterator();
-        while (it.hasNext()) {
-            it.next().drawLens(g);
-        }
-    }
-
-    void drawBoxes(Graphics g) {
-        Iterator<Lens> it = parent.sv_lensList.iterator();
-        while (it.hasNext()) {
-            Lens lens = it.next();
-            lens.drawBox(g, lens == parent.selectedLens);
-        }
-    }
-
-    // create two generic default lenses
-    void makeDefaultLenses() {
-        if (parent.sv_lensList.size() == 0) {
-            double ior = 1.52;
-            double disp = 59;
-            double cf = 0.03;
-            boolean leftHyp = false;
-            boolean rightHyp = false;
-            boolean symmetrical = true;
-
-            parent.sv_lensList.add(new Lens(parent, this, 0, 0, 2, 6, 6, 0, ior, cf, cf, disp, leftHyp, rightHyp, symmetrical));
-            parent.sv_lensList.add(new Lens(parent, this, 4, 0, 2, -6, -6, 0, ior, cf, cf, disp, leftHyp, rightHyp, symmetrical));
-            //rayTraceProcess(true);
-        }
-    }
-
-    public void traceRays(Graphics g, int width, int height) {
-        // generate some rays
-        Color pbcol = parent.sv_beamColor.getColor();
-        double min = parent.yStartBeamPos;
-        double max = parent.yEndBeamPos;
-        double xs = parent.xBeamSourceRefPlane;
-        double xt = parent.xBeamTargetRefPlane;
-        g.setColor(parent.sv_barrierColor.getColor());
-        drawScaledLine(xs, min * 100, ix, iy, g, parent.beamWidth, false);
-        drawScaledLine(xs, max * 100, ix, iy, g, parent.beamWidth, true);
-        drawScaledLine(xt, min * 100, ix, iy, g, parent.beamWidth, false);
-        drawScaledLine(xt, max * 100, ix, iy, g, parent.beamWidth, true);
-        if (parent.beamCount > 1) {
-            double fact = (max - min) / ((double) parent.beamCount - 1);
-            //double fact = (max - min) / ((double) parent.beamCount);
-            double y;
-            double xSource = parent.xBeamSourceRefPlane;
-            double xTarget = parent.xBeamTargetRefPlane;
-            double ba = -parent.beamAngle * toRadians;
-            // this value must not be zero
-            ba = (ba == 0.0) ? 1e-12 : ba;
-            for (int i = 0; (y = min + i * fact) <= max; i++) {
-                double mya = (parent.divergingSource) ? 0 : y;
-                double myb = (parent.divergingSource) ? ((parent.rotXZero) ? y * (xTarget - xSource) / xSource : y) : y;
-                mya += Math.tan(ba) * ((parent.rotXZero)
-                        ? xSource
-                        : (xSource - xTarget));
-                myb += Math.tan(ba) * ((parent.rotXZero)
-                        ? xTarget
-                        : 0);
-                if (parent.dispersionBeams > 1) {
-                    for (int j = 0; j < parent.dispersionBeams; j++) {
-                        double wvl = (((double) j / (parent.dispersionBeams - 1)) * (650.0 - 400.0)) + 400.0;
-                        ColorPos cp = createColorForWvl(wvl);
-                        MyColor beamColor = new MyColor(cp.r, cp.g, cp.b);
-                        traceOneRay(width, height, xSource, mya, xTarget, myb, g, parent.sv_lensList, beamColor, wvl);
-                    }
-                } else { // no dispersion
-                    traceOneRay(width, height, xSource, mya, xTarget, myb, g, parent.sv_lensList, pbcol, 0);
-                }
-            }
-        }
-    }
-
-    void traceOneRay(
-            int width,
-            int height,
-            double x1,
-            double y1,
-            double x2,
-            double y2,
-            Graphics g,
-            Vector<Lens> lensVec,
-            Color beamColor,
-            double wavelength) {
-        ComplexNum v;
-        Lens lens;
-        RayLensIntersection rayLensIntersection;
-        Color intCol = parent.sv_intersectionColor.getColor();
-
-        ComplexNum linea = new ComplexNum(x1, y1);
-        ComplexNum lineb = new ComplexNum(x2, y2);
-        double air_ior = 1.0;
-        boolean intersecting = true;
-        int rays = 0;
-        while (intersecting && rays++ < parent.maxIntersections) {
-            Vector<RayLensIntersection> intersections = new Vector<RayLensIntersection>();
-            Iterator<Lens> vli = lensVec.iterator();
-            while (vli.hasNext()) {
-                lens = vli.next();
-                for (int prof = 0; prof <= 1; prof++) {
-                    v = lens.profile(prof);
-                    lens.computeIntersections(prof, linea.x, linea.y, lineb.x, lineb.y, v.x, v.y, xa, ya, xb, yb);
-                    if (lens.inside(xa.v, ya.v)) {
-                        intersections.add(new RayLensIntersection(prof, xa.v, ya.v, lens));
-                    }
-                    if (lens.inside(xb.v, yb.v)) {
-                        intersections.add(new RayLensIntersection(prof, xb.v, yb.v, lens));
-                    }
-                }
-            }
-            int pointRad = (int) parent.intersectionDotRadius;
-            drawScaledPoint(linea.x, linea.y, pointRad, g, intCol);
-            Collections.sort(intersections, new IntersectionSortComparator());
-            // search for the next lens surface to the right (+x)
-            Iterator<RayLensIntersection> it = intersections.iterator();
-            intersecting = false;
-            rayLensIntersection = null;
-            while (it.hasNext()) {
-                rayLensIntersection = it.next();
-                if (rayLensIntersection.x > linea.x + epsilon) {
-                    intersecting = true;
-                    break;
-                }
-            }
-            g.setColor(beamColor);
-            if (intersecting) {
-                drawScaledLine(linea.x, linea.y, ix, iy, g, parent.beamWidth, false);
-                drawScaledLine(rayLensIntersection.x, rayLensIntersection.y, ix, iy, g, parent.beamWidth, true);
-
-                // beam incident angle
-                double ia = Math.atan2(lineb.y - linea.y, lineb.x - linea.x);
-
-                // surface is tangent to lens intersection point
-                // and therefore is the first derivative of that point
-                double my = rayLensIntersection.lens.tangent(rayLensIntersection.profile, rayLensIntersection.y);
-                // surface angle
-                double sa = Math.atan(my);
-
-                // Snell's Law calculation block
-                double abbe = rayLensIntersection.lens.dispersion;
-                double mediaIor = (wavelength == 0 || abbe == 0) ? rayLensIntersection.lens.ior : dispersionIndex(rayLensIntersection.lens.ior, wavelength, abbe);
-                double n1, n2;
-                if (rayLensIntersection.profile == 1) {
-                    // if air -> lens transition
-                    n1 = air_ior;
-                    n2 = mediaIor;
-                } else {
-                    // lens -> air transition
-                    n1 = mediaIor;
-                    n2 = air_ior;
-                }
-
-                // 1. add surface angle to incident angle
-                double a1 = ia + sa;
-                // 2. perform Snell's Law calculation
-                double a2 = Math.asin(n1 * Math.sin(a1) / n2);
-                // 3. subtract surface angle
-                a2 -= sa;
-
-                // create new rectangular vector using result angle
-                double nlx = Math.cos(a2);
-                double nly = Math.sin(a2);
-
-                // update vectors
-                linea.x = rayLensIntersection.x;
-                linea.y = rayLensIntersection.y;
-                lineb.x = linea.x + nlx;
-                lineb.y = linea.y + nly;
-
-            } else { // no intersection, so draw terminating lines at right
-                double x = parent.xBeamTargetRefPlane;
-                double y = 0;
-                //displayToSpaceOffset(x, y, dx, dy);
-                double yy = y_intercept(linea.x, linea.y, lineb.x, lineb.y, x);
-                drawScaledLine(linea.x, linea.y, ix, iy, g, parent.beamWidth, false);
-                drawScaledLine(x, yy, ix, iy, g, parent.beamWidth, true);
-                drawScaledPoint(x, yy, pointRad, g, intCol);
-            }
-        } // while rays++ < parent.maxIntersections
-    }
-
-    void drawBaselines(Graphics g) {
-        // y = 0 line
-        double x1 = 0;
-        double x2 = parent.xSize;
-        double y = 0;
-        displayToSpaceOffset(x1, y, dx1, dy);
-        displayToSpaceOffset(x2, y, dx2, dy);
-        g.setColor(parent.sv_yBaselineColor.getColor());
-        drawScaledLine(dx1.v, y, ix, iy, g, 1, false);
-        drawScaledLine(dx2.v, y, ix, iy, g, 1, true);
-        // x = 0 line
-        double y1 = 0;
-        double y2 = parent.ySize;
-        double x = 0;
-        displayToSpaceOffset(x, y1, dx, dy1);
-        displayToSpaceOffset(x, y2, dx, dy2);
-        drawScaledLine(x, dy1.v, ix, iy, g, 1, false);
-        drawScaledLine(x, dy2.v, ix, iy, g, 1, true);
-    }
-
-    void drawGrid(Graphics g) {
-        double x1 = 0;
-        double x2 = parent.xSize;
-        double y1 = 0;
-        double y2 = parent.ySize;
-        displayToSpaceOffset(x1, y1, dx1, dy1);
-        displayToSpaceOffset(x2, y2, dx2, dy2);
-        double fact = 2.0; // increases the density of grid lines
-        double e = Math.log10(parent.sv_dispScale * fact) - 100.0;
-        e = e - (e % 1.0) + 100.0;
-        double step = Math.pow(10.0, -e);
-        double xstart = gridRound(dx1.v, step) - step;
-        double xend = dx2.v + step;
-        double ystart = gridRound(dy2.v, step) - step;
-        double yend = dy1.v + step;
-        g.setColor(parent.sv_gridColor.getColor());
-        double x, y;
-        for (int j = 0; (y = ystart + j * step) <= yend; j++) {
-            for (int k = 0; (x = xstart + k * step) <= xend; k++) {
-                drawScaledLine(x, ystart, ix, iy, g, 1, false);
-                drawScaledLine(x, yend, ix, iy, g, 1, true);
-            }
-        }
-        for (int j = 0; (x = xstart + j * step) <= xend; j++) {
-            for (int k = 0; (y = ystart + k * step) <= yend; k++) {
-                drawScaledLine(xstart, y, ix, iy, g, 0, false);
-                drawScaledLine(xend, y, ix, iy, g, 0, true);
-            }
-        }
-    }
-
-    double gridRound(double v, double modulus, boolean roundUp) {
-        int sign = (v < 0) ? -1 : 1;
-        v = Math.abs(v);
-        if (roundUp) {
-            v = v + modulus;
-        }
-        v = v - v % modulus;
-        v *= sign;
-        return v;
-    }
-
-    double gridRound(double v, double modulus) {
-        return gridRound(v, modulus, false);
-    }
-
-    ColorPos createColorForWvl(double w) {
-        int j;
-        for (j = 0; j < 6; j++) {
-            if (w > colors[j + 1].p && w <= colors[j].p) {
-                break;
-            }
-        }
-        double r = ntrp(w, colors[j].p, colors[j + 1].p, colors[j].r, colors[j + 1].r);
-        double g = ntrp(w, colors[j].p, colors[j + 1].p, colors[j].g, colors[j + 1].g);
-        double b = ntrp(w, colors[j].p, colors[j + 1].p, colors[j].b, colors[j + 1].b);
-        return new ColorPos(r, g, b, 0);
-    }
-
-    // dior = ior + ((dp - w) * 500000) / (abbe * dp * w^2)
-    double dispersionIndex(double ior, double wavelength, double abbe) {
-        return ior + ((dispersionPivot - wavelength) * dispersionFactor) / (abbe * dispersionPivot * wavelength * wavelength);
-    }
 
-    // line (x1,y1 ... x2,y2) circle (radius, center x center y),
-    // intersection points xa,ya ... xb,yb
-    void circle_line_intersections(
-            double x1,
-            double y1,
-            double x2,
-            double y2,
-            double r,
-            double cx,
-            double cy,
-            MutableDouble xa,
-            MutableDouble ya,
-            MutableDouble xb,
-            MutableDouble yb) {
-        if (y1 == y2) {
-            y2 += 1e-15;
-        }
-        x1 -= cx;
-        x2 -= cx;
-        y1 -= cy;
-        y2 -= cy;
-
-        double term1 = Math.sqrt(Math.pow(y1 - y2, 2) * (Math.pow(r, 2) * (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) - Math.pow(x2 * y1 - x1 * y2, 2)));
-        double term2 = (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) * (y1 - y2);
-        double term3 = (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
-
-        xa.v = (x2 * y1 * Math.pow(y1 - y2, 2) - x2 * term1 + x1 * (-(Math.pow(y1 - y2, 2) * y2) + term1)) / (term2);
-        xb.v = (-(x1 * Math.pow(y1 - y2, 2) * y2) - x1 * term1 + x2 * (y1 * Math.pow(y1 - y2, 2) + term1)) / (term2);
-
-        ya.v = ((x1 - x2) * (-(x2 * y1) + x1 * y2) + term1) / term3;
-        yb.v = ((x1 - x2) * (-(x2 * y1) + x1 * y2) - term1) / term3;
-
-        // preserve these non-optimized originals in case of difficulty
-        //xa.v = (x2 * y1 * Math.pow(y1 - y2, 2) - x2 * Math.sqrt(Math.pow(y1 - y2, 2) * (Math.pow(r, 2) * (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) - Math.pow(x2 * y1 - x1 * y2, 2))) + x1 * (-(Math.pow(y1 - y2, 2) * y2) + Math.sqrt(Math.pow(y1 - y2, 2) * (Math.pow(r, 2) * (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) - Math.pow(x2 * y1 - x1 * y2, 2))))) / ((Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) * (y1 - y2));
-        //ya.v = ((x1 - x2) * (-(x2 * y1) + x1 * y2) + Math.sqrt(Math.pow(y1 - y2, 2) * (Math.pow(r, 2) * (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) - Math.pow(x2 * y1 - x1 * y2, 2)))) / (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
-        //xb.v = (-(x1 * Math.pow(y1 - y2, 2) * y2) - x1 * Math.sqrt(Math.pow(y1 - y2, 2) * (Math.pow(r, 2) * (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) - Math.pow(x2 * y1 - x1 * y2, 2))) + x2 * (y1 * Math.pow(y1 - y2, 2) + Math.sqrt(Math.pow(y1 - y2, 2) * (Math.pow(r, 2) * (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) - Math.pow(x2 * y1 - x1 * y2, 2))))) / ((Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) * (y1 - y2));
-        //yb.v = ((x1 - x2) * (-(x2 * y1) + x1 * y2) - Math.sqrt(Math.pow(y1 - y2, 2) * (Math.pow(r, 2) * (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) - Math.pow(x2 * y1 - x1 * y2, 2)))) / (Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
-
-        xa.v += cx;
-        xb.v += cx;
-        ya.v += cy;
-        yb.v += cy;
-    }
-
-    // line (x1,y1 ... x2,y2) curvature factor (cf),
-    // hyp radius & depth (r, d), center x and y (cx,cy)
-    // intersection points xa,ya ... xb,yb
-    void hyp_line_intersections(
-            double x1,
-            double y1,
-            double x2,
-            double y2,
-            double cf,
-            double r,
-            double d,
-            double cx,
-            double cy,
-            MutableDouble xa,
-            MutableDouble ya,
-            MutableDouble xb,
-            MutableDouble yb) {
-
-        x1 -= cx;
-        x2 -= cx;
-        y1 -= cy;
-        y2 -= cy;
-
-        double term1 = 2 * Math.pow(d, 2) * (y1 - y2) * (x2 * y1 - x1 * y2);
-        double term2 = Math.sqrt(-(Math.pow(d, 2) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * ((-1 + 4 * cf) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - 4 * (Math.pow(d, 2) * Math.pow(y1 - y2, 2) + d * (y1 - y2) * (x2 * y1 - x1 * y2) + cf * Math.pow(x2 * y1 - x1 * y2, 2)))));
-        double term3 = Math.sqrt(-(Math.pow(d, 2) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * ((-1 + 4 * cf) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - 4 * Math.pow(d, 2) * Math.pow(y1 - y2, 2) - 4 * d * (y1 - y2) * (x2 * y1 - x1 * y2) - 4 * cf * Math.pow(x2 * y1 - x1 * y2, 2))));
-        double term4 = (2. * (cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - Math.pow(d, 2) * Math.pow(y1 - y2, 2)));
-        double term5 = (2. * (x1 - x2) * (cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - Math.pow(d, 2) * Math.pow(y1 - y2, 2)));
-
-        xa.v = (-(d * Math.pow(r, 2) * Math.pow(x1 - x2, 2)) - term1 + term2) / term4;
-        xb.v = -(d * Math.pow(r, 2) * Math.pow(x1 - x2, 2) + term1 + term2) / term4;
-        ya.v = (d * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-y1 + y2) + 2 * cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-(x2 * y1) + x1 * y2) + (y1 - y2) * term3) / term5;
-        yb.v = (d * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-y1 + y2) + 2 * cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-(x2 * y1) + x1 * y2) + (-y1 + y2) * term3) / term5;
-
-
-        // preserve these non-optimized originals in case of difficulty
-        //xa.v = (-(d * Math.pow(r, 2) * Math.pow(x1 - x2, 2)) - 2 * Math.pow(d, 2) * (y1 - y2) * (x2 * y1 - x1 * y2) + Math.sqrt(-(Math.pow(d, 2) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * ((-1 + 4 * cf) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - 4 * (Math.pow(d, 2) * Math.pow(y1 - y2, 2) + d * (y1 - y2) * (x2 * y1 - x1 * y2) + cf * Math.pow(x2 * y1 - x1 * y2, 2)))))) / (2. * (cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - Math.pow(d, 2) * Math.pow(y1 - y2, 2)));
-        //ya.v = (d * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-y1 + y2) + 2 * cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-(x2 * y1) + x1 * y2) + (y1 - y2) * Math.sqrt(-(Math.pow(d, 2) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * ((-1 + 4 * cf) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - 4 * Math.pow(d, 2) * Math.pow(y1 - y2, 2) - 4 * d * (y1 - y2) * (x2 * y1 - x1 * y2) - 4 * cf * Math.pow(x2 * y1 - x1 * y2, 2))))) / (2. * (x1 - x2) * (cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - Math.pow( [...]
-        //xb.v = -(d * Math.pow(r, 2) * Math.pow(x1 - x2, 2) + 2 * Math.pow(d, 2) * (y1 - y2) * (x2 * y1 - x1 * y2) + Math.sqrt(-(Math.pow(d, 2) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * ((-1 + 4 * cf) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - 4 * (Math.pow(d, 2) * Math.pow(y1 - y2, 2) + d * (y1 - y2) * (x2 * y1 - x1 * y2) + cf * Math.pow(x2 * y1 - x1 * y2, 2)))))) / (2. * (cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - Math.pow(d, 2) * Math.pow(y1 - y2, 2)));
-        //yb.v = (d * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-y1 + y2) + 2 * cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * (-(x2 * y1) + x1 * y2) + (-y1 + y2) * Math.sqrt(-(Math.pow(d, 2) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) * ((-1 + 4 * cf) * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - 4 * Math.pow(d, 2) * Math.pow(y1 - y2, 2) - 4 * d * (y1 - y2) * (x2 * y1 - x1 * y2) - 4 * cf * Math.pow(x2 * y1 - x1 * y2, 2))))) / (2. * (x1 - x2) * (cf * Math.pow(r, 2) * Math.pow(x1 - x2, 2) - Math.pow [...]
-
-        xa.v += cx;
-        xb.v += cx;
-        ya.v += cy;
-        yb.v += cy;
-    }
-
-    //      x1 (x4 (-y2 + y3) + x3 (y2 - y4)) + x2 (x4 (y1 - y3) + x3 (-y1 + y4))
-    // xi = ---------------------------------------------------------------------
-    //                 -((x3 - x4) (y1 - y2)) + (x1 - x2) (y3 - y4)
-    double line_line_intersection(
-            double ax1, double ay1, double ax2, double ay2, double bx1, double by1, double bx2, double by2) {
-        return (ax1 * (ay2 * (bx1 - bx2) + bx2 * by1 - bx1 * by2) + ax2 * (ay1 * (-bx1 + bx2) - bx2 * by1 + bx1 * by2)) / (-((ay1 - ay2) * (bx1 - bx2)) + (ax1 - ax2) * (by1 - by2));
-    }
-
-    //           x2 (-y1 + y2)   xi (-y1 + y2)
-    // yi = y2 - ------------- + -------------
-    //            -x1 + x2        -x1 + x2
-    double y_intercept(
-            double x1, double y1, double x2, double y2, double xi) {
-        return y1 - (x1 * (-y1 + y2)) / (-x1 + x2) + (xi * (-y1 + y2)) / (-x1 + x2);
-    }
-
-    double ntrp(double x, double xa, double xb, double ya, double yb) {
-        return ((x - xa) / (xb - xa)) * (yb - ya) + ya;
-    }
-
-    void spaceToDisplay(double x, double y, MutableDouble dx, MutableDouble dy) {
-        dx.v = ((x - parent.sv_xOffset) * parent.sv_dispScale * parent.ySize) + parent.xCenter;
-        dy.v = parent.yCenter - ((y - parent.sv_yOffset) * parent.sv_dispScale * parent.ySize);
-    }
-
-    void spaceToDisplay(double x, double y, MutableInt dx, MutableInt dy) {
-        dx.v = (int) (((x - parent.sv_xOffset) * parent.sv_dispScale * parent.ySize) + parent.xCenter);
-        dy.v = (int) (parent.yCenter - ((y - parent.sv_yOffset) * parent.sv_dispScale * parent.ySize));
-    }
-
-    void displayToSpaceOffset(double dx, double dy, MutableDouble x, MutableDouble y) {
-        x.v = ((dx - parent.xCenter) / (parent.sv_dispScale * parent.ySize)) + parent.sv_xOffset;
-        y.v = ((parent.yCenter - dy) / (parent.sv_dispScale * parent.ySize)) + parent.sv_yOffset;
-    }
-
-    void displayToSpace(double dx, double dy, MutableDouble x, MutableDouble y) {
-        x.v = ((dx - parent.xCenter) / (parent.sv_dispScale * parent.ySize));
-        y.v = ((parent.yCenter - dy) / (parent.sv_dispScale * parent.ySize));
-    }
-
-    void scalePoint(double x, double y, MutableInt dx, MutableInt dy) {
-        //CartesianInt ci = new CartesianInt();
-        spaceToDisplay(x, y, dx, dy);
-        //ci.x = (int) cd.x;
-        //ci.y = (int) cd.y;
-    }
-
-    void drawScaledPoint(double x, double y, double radius, Graphics g, Color col) {
-        boolean filled = (radius < 0);
-        radius = Math.abs(radius);
-        g.setColor(col);
-        //int ix,iy;
-        scalePoint(x, y, ipx, ipy);
-        int r = (int) radius;
-        if (filled) {
-            g.fillOval(ipx.v - r / 2, ipy.v - r / 2, r, r);
-        } else {
-            g.drawOval(ipx.v - r / 2, ipy.v - r / 2, r, r);
-        }
-    }
-
-    void drawScaledLine(double x, double y, MutableInt opx, MutableInt opy, Graphics g, double width, boolean draw) {
-
-        scalePoint(x, y, ipx, ipy);
-        if (draw) {
-            g.drawLine(opx.v, opy.v, ipx.v, ipy.v);
-        }
-        opx.v = ipx.v;
-        opy.v = ipy.v;
-    }
+package opticalraytracer;
 
-    double sphericalLensProfileXforY(double y, double cx, double sphereRadius, double offset, double thickness) {
-        double v = Math.sqrt(sphereRadius * sphereRadius - y * y);
-        double q = ((sphereRadius < 0) ? -offset + v + thickness : offset - v + thickness);
-        return q + cx;
-    }
+import static java.lang.Math.*;
 
-    // 1st derivative
-    double sphericalLensProfileDYforY(double y, double cx, double sphereRadius, double offset, double thickness) {
-        double r = sphereRadius;
-        double v = -(y / Math.sqrt(Math.pow(r, 2) - Math.pow(y, 2)));
-        if (sphereRadius < 0) {
-            v = -v;
-        }
-        return v;
-    }
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.util.ArrayList;
+import java.util.Collections;
 
-    double hyperbolicLensProfileXforY(double y, double cx, double cf, double sphereRadius, double offset, double thickness) {
-        double r = Math.sqrt(sphereRadius * sphereRadius - offset * offset);
-        double rad = sphereRadius;
-        rad = (rad < 0) ? -rad : rad;
-        double d = rad - offset;
-        double v = (-(d * Math.pow(r, 2)) + Math.sqrt(Math.pow(d, 2) * Math.pow(r, 2) * ((1 - 4 * cf) * Math.pow(r, 2) + 4 * cf * Math.pow(y, 2)))) / (2. * cf * Math.pow(r, 2));
-        if (sphereRadius < 0) {
-            v = -v;
-        }
-        return thickness + v + cx;
-    }
+final public class RayTraceComputer {
 
-    // 1st derivative
-    double hyperbolicLensProfileDYforY(double y, double cx, double cf, double sphereRadius, double offset, double thickness) {
-        double r = Math.sqrt(sphereRadius * sphereRadius - offset * offset);
-        double rad = sphereRadius;
-        rad = (rad < 0) ? -rad : rad;
-        double d = rad - offset;
-        double v = -(2 * Math.pow(d, 2) * y) / Math.sqrt(Math.pow(d, 2) * Math.pow(r, 2) * ((1 - 4 * cf) * Math.pow(r, 2) + 4 * cf * Math.pow(y, 2)));
-        if (sphereRadius < 0) {
-            v = -v;
-        }
-        return v;
-    }
+	OpticalRayTracer parent;
+	ProgramValues programValues;
+	ArrayList<LineData> lineList;
+	int testCount = 0;
+	Vector[] arrowLines;
+
+	public RayTraceComputer(OpticalRayTracer p) {
+		parent = p;
+		programValues = p.programValues;
+		// polygon vertices for arrow
+		arrowLines = new Vector[] { new Vector(0, 0), new Vector(-1, .5),
+				new Vector(-1, -.5) };
+
+	}
+
+	void drawLenses(Graphics2D g) {
+		for (OpticalComponent oc : parent.componentList) {
+			oc.drawLens(g);
+		}
+	}
+
+	void drawGrid(Graphics2D g) {
+		Vector p1 = parent.displayToSpaceOffset(new Vector(0, 0));
+		Vector p2 = parent.displayToSpaceOffset(new Vector(parent.xSize,
+				parent.ySize));
+		double fact = 2.0; // increases the density of grid lines
+		double e = log(programValues.dispScale * fact) / log(5) - 100.0;
+		e = e - (e % 1.0) + 100.0;
+		double step = pow(5.0, -e);
+		double xstart = gridRound(p1.x, step) - step;
+		double xend = p2.x + step;
+		double ystart = gridRound(p2.y, step) - step;
+		double yend = p1.y + step;
+		Color col = new MyColor(programValues.colorGrid);
+		g.setColor(col);
+		ComplexInt i = new ComplexInt();
+		double x, y;
+		for (int j = 0; (y = ystart + j * step) <= yend; j++) {
+			for (int k = 0; (x = xstart + k * step) <= xend; k++) {
+				drawScaledLine(x, ystart, i, g, false);
+				drawScaledLine(x, yend, i, g, true);
+			}
+		}
+		for (int j = 0; (x = xstart + j * step) <= xend; j++) {
+			for (int k = 0; (y = ystart + k * step) <= yend; k++) {
+				drawScaledLine(xstart, y, i, g, false);
+				drawScaledLine(xend, y, i, g, true);
+			}
+		}
+	}
+
+	double gridRound(double v, double modulus, boolean roundUp) {
+		int sign = (v < 0) ? -1 : 1;
+		v = abs(v);
+		if (roundUp) {
+			v = v + modulus;
+		}
+		v = v - v % modulus;
+		v *= sign;
+		return v;
+	}
+
+	void fillScaledPoint(Vector p, double radius, Graphics2D g, MyColor col) {
+		boolean filled = true;
+		// Color alphaColor = new Color(col.getRed(), col.getGreen(),
+		// col.getBlue(), 128);
+		radius = abs(radius);
+		g.setColor(col);
+		// int ix,iy;
+		ComplexInt ip = scalePoint(p);
+		int r = (int) radius;
+		if (filled) {
+			g.fillOval(ip.x - r / 2, ip.y - r / 2, r, r);
+		} else {
+			g.drawOval(ip.x - r / 2, ip.y - r / 2, r, r);
+		}
+	}
+
+	void drawArrowhead(Vector a, double angle, double radius, Graphics2D g,
+			Color col) {
+		if (a != null) {
+			Polygon p = new Polygon();
+			for (Vector op : arrowLines) {
+				Vector dp = op.scale(radius).rotate(angle).translate(a);
+				ComplexInt ip = scalePoint(dp);
+				p.addPoint(ip.x, ip.y);
+			}
+			g.setColor(col);
+			g.fillPolygon(p);
+		}
+	}
+
+	void drawScaledLine(Vector p, ComplexInt op, Graphics2D g, boolean draw) {
+		drawScaledLine(p.x, p.y, op, g, draw);
+	}
+
+	void drawScaledLine(double x, double y, ComplexInt op, Graphics2D g,
+			boolean draw) {
+		if (Double.isNaN(x) || Double.isNaN(y)) {
+			return;
+		}
+		y = min(y, parent.programValues.virtualSpaceSize);
+		y = max(y, -parent.programValues.virtualSpaceSize);
+		x = min(x, parent.programValues.virtualSpaceSize);
+		x = max(x, -parent.programValues.virtualSpaceSize);
+		ComplexInt sp = scalePoint(x, y);
+		if (draw) {
+			g.drawLine(op.x, op.y, sp.x, sp.y);
+		}
+		op.assign(sp);
+	}
+
+	void addToPolygon(Polygon p, double x, double y) {
+
+		ComplexInt p1 = scalePoint(x, y);
+		p.addPoint(p1.x, p1.y);
+	}
+
+	ComplexInt scalePoint(double x, double y) {
+		return parent.spaceToDisplay(x, y);
+	}
+
+	ComplexInt scalePoint(Vector p) {
+		return parent.spaceToDisplay(p.x, p.y);
+	}
+
+	double gridRound(double v, double modulus) {
+		return gridRound(v, modulus, false);
+	}
+
+	void drawBaselines(Graphics2D g) {
+		// y = 0 line
+		double x1 = 0;
+		double x2 = parent.xSize;
+		double y = 0;
+		Vector p1 = parent.displayToSpaceOffset(new Vector(x1, y));
+		Vector p2 = parent.displayToSpaceOffset(new Vector(x2, y));
+		Color col = new MyColor(programValues.colorBaseline);
+		g.setColor(col);
+		ComplexInt i = new ComplexInt();
+		drawScaledLine(p1.x, y, i, g, false);
+		drawScaledLine(p2.x, y, i, g, true);
+		// x = 0 line
+		double y2 = parent.ySize;
+		double x = 0;
+		p1 = parent.displayToSpaceOffset(new Vector(x1, y));
+		p2 = parent.displayToSpaceOffset(new Vector(x, y2));
+		drawScaledLine(x, p1.y, i, g, false);
+		drawScaledLine(x, p2.y, i, g, true);
+	}
+
+	public void traceRays(Graphics2D g2d, boolean collectLines) {
+		if (collectLines) {
+			lineList = new ArrayList<>();
+		}
+		// parent.p("traceRays: " + testCount);
+		// testCount += 1;
+		// generate some rays
+
+		double xSource = programValues.xBeamSourceRefPlane;
+		double xTarget = programValues.xBeamRotationPlane;
+		double ba = -programValues.beamAngle * Common.radians;
+		double tba = tan(ba) * (xSource - xTarget);
+		double min = programValues.yStartBeamPos;
+		double max = programValues.yEndBeamPos;
+		double rmin = min + tba;
+		double rmax = max + tba;
+		double xs = programValues.xBeamSourceRefPlane;
+		if (!collectLines) {
+			g2d.setColor(new MyColor(programValues.colorLightSource));
+			ComplexInt i = new ComplexInt();
+			drawScaledLine(xs, rmin, i, g2d, false);
+			drawScaledLine(xs, rmax, i, g2d, true);
+		}
+
+		double count = max(programValues.beamCount, 1);
+		double topcount = max(programValues.beamCount - 1, 1);
+		for (int ray = 0; ray < count && ray < parent.maxLightRays; ray++) {
+			double y = Common.ntrp(ray, 0, topcount, min, max);
+			double mya = (programValues.divergingSource) ? 0 : y;
+			double myb = y;
+			mya += tba;
+			Color term = new MyColor(programValues.colorTerminator);
+			Color pbcol = new MyColor(programValues.colorBeam);
+			Color arrowCol = new MyColor(programValues.colorArrow);
+			// an alpha value used by the dispersion calculation
+			double pbalpha = pbcol.getAlpha() / 255.0;
+			if (programValues.dispersionBeams > 0) {
+				for (int dbeam = 0; dbeam < programValues.dispersionBeams; dbeam++) {
+					double top = max(programValues.dispersionBeams - 1, 1);
+					// h = hue component of HSV
+					double h = Common.ntrp(dbeam, 0, top, 0, 1);
+					WavelengthColor cw = new WavelengthColor(h);
+					// borrow alpha from normal beam color
+					MyColor colorWavelength = new MyColor(cw.r, cw.g, cw.b,
+							pbalpha);
+					traceOneRay(ray, dbeam, xSource, mya, xTarget, myb, g2d,
+							parent.componentList, term, colorWavelength,
+							arrowCol, cw.wvl, programValues.maxIntersections,
+							collectLines);
+				}
+			} else { // no dispersion
+				traceOneRay(ray, 0, xSource, mya, xTarget, myb, g2d,
+						parent.componentList, term, pbcol, arrowCol, 0,
+						programValues.maxIntersections, collectLines);
+			}
+		}
+	}
+
+	boolean testIntersection(RayLensIntersection r) {
+		return ((r.dot > 0) && (r.m > parent.programValues.interLensEpsilon));
+	}
+
+	void traceOneRay(int ray, int dbeam, double x1, double y1, double x2,
+			double y2, Graphics2D g2d, ArrayList<OpticalComponent> componentList,
+			Color terminalColor, Color beamColor, Color arrowColor,
+			double wavelength, int maxIntersections, boolean collectLines) {
+		//Common.p("------------------------------------------------------");
+		//testCount += 1;
+		// int interactions = 0;
+		double arrowRadius = programValues.intersectionArrowSize
+				/ sqrt(programValues.dispScale);
+		boolean drawing = true;
+
+		ComplexInt op = new ComplexInt();
+		double oldAngle = 0;
+		Vector linea = new Vector(x1, y1);
+		Vector lineb = new Vector(x2, y2);
+		RayLensIntersection oldrli = null;
+		RayLensIntersection rli = null;
+		double airIOR = 1.0;
+		// initial assumption is that we start in open air
+		double oldIOR = airIOR;
+		boolean intersecting = true;
+		// entering means moving from air to medium IOR
+		boolean entering = true;
+		boolean reflector = false;
+		int rays = 0;
+		String oldEvent = "Beam Origin";
+		String newEvent = oldEvent;
+		double initialBeamAngle = atan2(lineb.y - linea.y, lineb.x - linea.x);
+		oldAngle = initialBeamAngle;
+		//boolean needRotation = false;
+		boolean internalReflection = false;
+		while (intersecting && rays++ < maxIntersections) {
+			//Common.p("------------------------------------------------------");
+			ArrayList<RayLensIntersection> intersections = new ArrayList<RayLensIntersection>();
+			for (OpticalComponent lens : componentList) {
+				if (lens.values.active) {
+					for (int i = 0; i <= 1; i++) {
+						// lens profile and intersection routines must be
+						// kept synchronized as to the lens side
+						lens.computeIntersections(oldrli, lens, i == 0, linea,
+								lineb);
+						for (Vector pt : lens.getElement(i == 0).getPoints()) {
+							if (lens.inside(pt, lens.opticalTestPolygon)) {
+								intersections.add(new RayLensIntersection(
+										linea, lineb, wavelength, pt, i == 0,
+										lens));
+							}
+						}
+
+					} // for i = 0; i <= 1
+				}
+			} // for lens : lensVec
+			if (!collectLines) {
+				drawArrowhead(linea, oldAngle, arrowRadius, g2d,
+						(wavelength != 0) ? beamColor : arrowColor);
+			}
+			oldrli = rli;
+			rli = null;
+			// sort the array based on range, nearest is first in array
+			Collections.sort(intersections, new IntersectionSortComparator());
+			// choose the next target
+			double n = 0;
+			double len = intersections.size();
+			boolean debugIntersections = false;
+			// parent.p("begin testing " + intersections.size());
+			for (RayLensIntersection r : intersections) {
+				// take the first target from the distance-sorted array
+				// that has an acceptable vector magnitude
+				// and whose vector points in the current direction
+				// parent.p("candidate: m: " + r.m + " dot: "+r.dot);
+				// parent.p("dot: " + r.dot + ", distance: " + r.m);
+				MyColor cc = null;
+				if (debugIntersections) {
+					cc = WavelengthColor.hToRGB(n * len / 2);
+					cc = new MyColor(cc.getRGB(), 64);
+					// parent.p("cc: " + cc);
+
+					if (testIntersection(r)) {
+						if (rli == null) {
+							rli = r;
+							// accepted target: green
+							cc = new MyColor(0x00ff00);
+						} else {
+							// passed up: purple
+							cc = new MyColor(0x80ff00ff);
+						}
+						if (!debugIntersections) {
+							break;
+						}
+					} 
+					if (debugIntersections && !collectLines) {
+						fillScaledPoint(r.p, 8, g2d, cc);
+					}
+				} else {
+					//Common.p("test r: " + r);
+					if (testIntersection(r)) {
+						rli = r;
+						break;
+					}
+				}
+				n += 1;
+			}
+			intersecting = (rli != null);
+			if (!collectLines) {
+				g2d.setColor(beamColor);
+			}
+
+			if (intersecting) {
+				double n1 = oldIOR, n2 = oldIOR;
+				reflector = rli.function == Common.OBJECT_REFLECTOR;
+				internalReflection = false;
+				if (!collectLines) {
+					drawScaledLine(linea, op, g2d, false);
+					drawScaledLine(rli.p, op, g2d, true);
+				}
+				// incident light angle vector
+				Vector via = lineb.sub(linea).normalize();
+				oldAngle = via.angle();
+				double la = rli.lens.angleRadians();
+				if (rli.function != Common.OBJECT_ABSORBER) {
+					// surface is tangent to lens intersection point
+					// so dx is the first derivative, the curvature, at that
+					// point
+					double dx = rli.lens.tangent(rli.leftSide, entering, rli.p,
+							la, reflector);
+					Vector vsa = Vector.polar(atan2(1,dx)+PI/2).rotate(la);
+					Vector sr = null;
+					// test for reflector
+					if (reflector) {
+						newEvent = "Reflection";
+						sr = Common.computeReflectionAngle(via, vsa);
+					} else {
+						// Snell's Law refraction calculation block
+						// a bit more complicated than reflection
+						double abbe = rli.lens.values.dispersion;
+						double mediaIOR = (wavelength == 0 || abbe == 0) ? rli.lens.values.ior
+								: WavelengthColor.dispersionIndex(
+										rli.lens.values.ior, wavelength, abbe);
+						n2 = (entering) ? mediaIOR : airIOR;
+						// the vector form of Snell's Law is required to deal with
+						// the case of acute angles between incident and surface normal
+						sr = (Common.snell2d(via,vsa,n1, n2));
+						//Common.p("entering: " + Common.pb(entering)
+						//		+ ", n1/n2: " + Common.pf(n1 / n2) + ", via: "
+						//		+ Common.pd(via.angle()) + ", vsa: " + Common.pd(vsa.angle())
+						//		+ ", sr: " + Common.pd(sr.angle()) + ", sr-via: "
+						//		+ Common.pd(sr.sub(via).angle()));
+						// 3. if computed angle is too acute, expect
+						// math domain error, which signals
+						// total internal reflection (TIR)
+						if (!sr.isValid()) {
+							// the result exceeds
+							// the critical angle of reflection
+							// so reflect the beam inside the lens
+							internalReflection = true;
+							newEvent = "Internal Reflection";
+							sr = Common.computeReflectionAngle(via, vsa);
+							//Common.p("NaN detected");
+						} else {
+							newEvent = "Refraction";
+						}
+					}
+					if (collectLines) {
+						lineList.add(new LineData(ray, dbeam, wavelength,
+								oldrli, rli, linea, rli.p, vsa.angle(), oldEvent,
+								newEvent));
+					}
+					// create new vector using Snell result angle
+					linea = new Vector(rli.p);
+					lineb = linea.add(sr);
+					// a reflector always has entering = true
+					if (!reflector && !internalReflection) {
+						entering = !entering;
+						oldIOR = n2;
+					}
+				} else {
+					newEvent = "Absorption";
+					// absorber, so terminate ray trace
+					if (!collectLines) {
+						// fillScaledPoint(rli.p, pointRad, g2d, terminalColor);
+						drawArrowhead(rli.p, oldAngle, arrowRadius, g2d,
+								terminalColor);
+					} else {
+						lineList.add(new LineData(ray, dbeam, wavelength,
+								oldrli, rli, linea, rli.p, 0, oldEvent,
+								newEvent));
+					}
+					drawing = false;
+					intersecting = false;
+				}
+			}
+			oldEvent = newEvent;
+		} // while rays++ < maxIntersections
+			// terminal line, to space boundary
+		if (drawing) {
+			newEvent = (rays >= maxIntersections) ? "Maximum Interaction Limit"
+					: "Termination";
+			// choose nearest space boundary
+			double xq1 = (lineb.x - linea.x > 0) ? programValues.virtualSpaceSize
+					: -programValues.virtualSpaceSize;
+			double yq1 = Common.ntrp(xq1, linea.x, lineb.x, linea.y, lineb.y);
+			double yq2 = (lineb.y - linea.y > 0) ? programValues.virtualSpaceSize
+					: -programValues.virtualSpaceSize;
+			double xq2 = Common.ntrp(yq2, linea.y, lineb.y, linea.x, lineb.x);
+			Vector p;
+			if (abs(yq1) > abs(xq2)) {
+				p = new Vector(xq2, yq2);
+			} else {
+				p = new Vector(xq1, yq1);
+			}
+			if (!collectLines) {
+				g2d.setColor(beamColor);
+				drawScaledLine(linea, op, g2d, false);
+				drawScaledLine(p, op, g2d, true);
+				double angle = atan2(p.y - linea.y, p.x - linea.x);
+				drawArrowhead(p, angle, arrowRadius, g2d, terminalColor);
+			} else {
+				lineList.add(new LineData(ray, dbeam, wavelength, oldrli, rli,
+						linea, p, 0, oldEvent, newEvent));
+			}
+		}
+	}
 }
diff --git a/src/opticalraytracer/StringComparatorNoCase.java b/src/opticalraytracer/StringComparatorNoCase.java
deleted file mode 100644
index 00a6b39..0000000
--- a/src/opticalraytracer/StringComparatorNoCase.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
- 
-package opticalraytracer;
-
-import java.util.*;
-
-/**
- *
- * @author lutusp
- */
-final public class StringComparatorNoCase implements Comparator<String> {
-
-    public int compare(String s1, String s2) {
-        return s1.toLowerCase().compareTo(s2.toLowerCase());
-    }
-}
diff --git a/src/opticalraytracer/StringEscapeUnescape.java b/src/opticalraytracer/StringEscapeUnescape.java
deleted file mode 100644
index 1deb936..0000000
--- a/src/opticalraytracer/StringEscapeUnescape.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-final public class StringEscapeUnescape {
-
-    static public String stringToHex(String s) {
-        char[] sa = s.toCharArray();
-        StringBuilder hex = new StringBuilder();
-        for (int i = 0; i < sa.length; i++) {
-            char c = sa[i];
-            String hs = Integer.toHexString(c);
-            if (hs.length() < 2) {
-                hs = "0" + hs;
-            }
-            hex.append(hs);
-        }
-        return hex.toString();
-    }
-
-    static public String hexToString(String hex) {
-        StringBuilder out = new StringBuilder();
-        try {
-        for(int i = 0;i < hex.length();i += 2) {
-            String hd = hex.substring(i,i+2);
-            char c = (char) Integer.parseInt(hd,16);
-            out.append(c);
-        }
-        }
-        catch(Exception e) {
-            System.out.println("hexToString: " + e);
-        }
-        return out.toString();
-    }
-}
diff --git a/src/opticalraytracer/UndoRedoPackage.java b/src/opticalraytracer/UndoRedoPackage.java
deleted file mode 100644
index a66e7b2..0000000
--- a/src/opticalraytracer/UndoRedoPackage.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package opticalraytracer;
-
-import java.util.*;
-
-/**
- *
- * @author lutusp
- */
-public class UndoRedoPackage {
-
-    String stringVec;
-    int isel = -1;
-
-    public UndoRedoPackage(Vector<Lens> lv, Lens sl) {
-        stringVec = lv.toString();
-        isel = lv.indexOf(sl);
-    }
-
-    public void restore(OpticalRayTracer p) {
-        p.sv_lensList = new Vector<Lens>();
-        String s = stringVec.replaceFirst("\\[(.*)\\]", "$1");
-        String[] array = s.split(",");
-        for (int i = 0; i < array.length; i++) {
-            p.sv_lensList.add(new Lens(p, p.rayTraceComputer, array[i]));
-        }
-        if (isel >= 0) {
-            p.setSelectedLens(p.sv_lensList.get(isel));
-        }
-        else {
-            p.setSelectedLens(null);
-        }
-        p.mouseTarget = null;
-    }
-}
diff --git a/src/opticalraytracer/UserActionManager.java b/src/opticalraytracer/UserActionManager.java
deleted file mode 100644
index 1e59594..0000000
--- a/src/opticalraytracer/UserActionManager.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2009 by Paul Lutus                                      *
- *   lutusp at arachnoid.com                                                  *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program 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 General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-package opticalraytracer;
-
-import javax.swing.*;
-import java.awt.event.*;
-import java.awt.*;
-
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-/**
- *
- * @author lutusp
- */
-final public class UserActionManager {
-
-    //int min, max;
-    JTextField field = null;
-    JComboBox box = null;
-    OpticalRayTracer parent;
-    double sens = 1;
-    double dmin, dmax;
-    int imin, imax;
-
-    public UserActionManager(double sens, double min, double max, JTextField field, OpticalRayTracer p) {
-        this.field = field;
-        dmin = min;
-        dmax = max;
-        init(sens, p);
-        assignHandlers(field);
-    }
-
-    public UserActionManager(JComboBox box, int min, int max, OpticalRayTracer p) {
-        this.box = box;
-        imin = min;
-        imax = max;
-        init(1, p);
-        assignHandlers(box);
-    }
-
-    void init(double sens, OpticalRayTracer p) {
-        parent = p;
-        this.sens = sens;
-    }
-
-    public void assignHandlers(Component comp) {
-        comp.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
-
-            public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
-                handleMouseWheelMoved(evt);
-            }
-        });
-        comp.addKeyListener(new java.awt.event.KeyAdapter() {
-
-            @Override
-            public void keyReleased(java.awt.event.KeyEvent evt) {
-                handleKeyReleased(evt);
-            }
-        });
-    }
-
-    private void handleKeyReleased(KeyEvent evt) {
-        int n = 0;
-        double sign = 0;
-        double v = sens;
-        v = (evt.isShiftDown()) ? v * 0.1 : v;
-        v = (evt.isAltDown()) ? v * 0.01 : v;
-        int kcode = evt.getKeyCode();
-        //String code = KeyEvent.getKey_Text(kcode);
-        //System.out.println("key: " + code);
-        if (kcode == KeyEvent.VK_ENTER) {
-            sign = 1;
-        } else if (kcode == KeyEvent.VK_HOME) {
-            n = 100;
-            sign = 1;
-        } else if (kcode == KeyEvent.VK_END) {
-            n = -100;
-            sign = 1;
-        } else if (kcode == KeyEvent.VK_PAGE_UP) {
-            n = 10;
-            sign = 1;
-        } else if (kcode == KeyEvent.VK_PAGE_DOWN) {
-            n = -10;
-            sign = 1;
-        } else if (kcode == KeyEvent.VK_DOWN) {
-            n = -1;
-            sign = 1;
-        } else if (kcode == KeyEvent.VK_UP) {
-            n = 1;
-            sign = 1;
-        } else if (kcode == KeyEvent.VK_ESCAPE) {
-            n = 0;
-            sign = -1;
-        }
-        handleIncrement(n, sign, v);
-    }
-
-    private void handleMouseWheelMoved(MouseWheelEvent evt) {
-        double v = sens;
-        v = (evt.isShiftDown()) ? v * 0.1 : v;
-        v = (evt.isAltDown()) ? v * 0.01 : v;
-        handleIncrement(-evt.getWheelRotation(), 1, v);
-    }
-
-    void handleIncrement(int n, double sign, double sv) {
-        if (sign != 0) {
-            if (field != null) {
-                String text = field.getText();
-                double dv = 0;
-                try {
-                    dv = parent.getDouble(text);
-                } catch (Exception e) {
-                    System.out.println(getClass().getName() + ": Error: " + e);
-                }
-                dv += (n * sv);
-                dv *= sign;
-                dv = Math.min(dmax, dv);
-                dv = Math.max(dmin, dv);
-                String s = parent.formatNum(dv);
-                field.setText(s);
-
-            } else if (box != null) {
-                int v = box.getSelectedIndex();
-                v += n;
-                int top = box.getItemCount();
-                v = (v < imin) ? imin : v;
-                v = (v >= imax) ? imax : v;
-                v = (v >= top) ? top - 1 : v;
-                box.setSelectedIndex(v);
-            }
-            if (parent != null) {
-                parent.readControls();
-            }
-        }
-    }
-}
diff --git a/src/opticalraytracer/ValueManager.java b/src/opticalraytracer/ValueManager.java
new file mode 100644
index 0000000..b484f56
--- /dev/null
+++ b/src/opticalraytracer/ValueManager.java
@@ -0,0 +1,123 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ValueManager {
+
+	String lineSep;
+
+	public ValueManager() {
+		lineSep = System.getProperty("line.separator");
+	}
+	
+	void p(String s) {
+		System.out.println(s);
+	}
+
+	protected String getValues() {
+		ArrayList<String> list = new ArrayList<>();
+		Field[] fields = getClass().getDeclaredFields();
+		for (Field f : fields) {
+			String tag = f.getName();
+			list.add(String.format("  %-25s = %s\n", tag, getOneValue(tag)));
+		}
+		Collections.sort(list);
+		StringBuilder sb = new StringBuilder();
+		for (String s : list) {
+			sb.append(s);
+		}
+		return sb.toString();
+	}
+
+	@SuppressWarnings("rawtypes")
+	protected String getOneValue(String tag) {
+		String result = "";
+		try {
+			Field f = getClass().getDeclaredField(tag);
+			//result = f.get(this).toString();
+			Class cls = f.getType();
+			if(cls == double.class) {
+				result = LocaleHandler.formatDouble(f.getDouble(this));
+			}
+			else if(cls == int.class) {
+				result = LocaleHandler.formatInt(f.getInt(this));
+			}
+			else {
+				result = f.get(this).toString();
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			
+		}
+		//p("valuemanager.getonevalue: tag: " + tag + ", value: " + result);
+		return result;
+	}
+
+	public String toString() {
+		return getValues();
+	}
+
+	protected boolean setValues(String s) {
+		boolean result = false;
+		if (s != null) {
+			String key,value;
+			String[] recs = s.split("\n");
+			for (String rec : recs) {
+				Pattern p = Pattern.compile("(.*?)\\s*=\\s*(.*)");
+				Matcher m = p.matcher(rec);
+				if(m.matches() && m.groupCount() == 2) {
+					 key = m.group(1).trim();
+					 value = m.group(2).trim();
+					setOneValue(key,value);
+					result = true;
+				}
+			}
+		}
+		return result;
+	}
+
+	@SuppressWarnings("rawtypes")
+	protected void setOneValue(String tag, String value) {
+		try {
+			Field f = getClass().getDeclaredField(tag);
+			Class cls = f.getType();
+			if (cls == boolean.class) {
+				f.setBoolean(this, value.matches("(?i).*true.*"));
+			} else if (cls == int.class) {
+				f.setInt(this, LocaleHandler.getInt(value));
+			} else if (cls == double.class) {
+				f.setDouble(this, LocaleHandler.getDouble(value,LocaleHandler.localeDecimalSeparator));
+			} else if (cls == String.class) {
+				f.set(this, value);
+			} else {
+				//throw new Exception("Cannot decode field type " + cls);
+			}
+		} catch (Exception e) {
+			//e.printStackTrace();
+		}
+	}
+}
diff --git a/src/opticalraytracer/Vector.java b/src/opticalraytracer/Vector.java
new file mode 100644
index 0000000..d7a261e
--- /dev/null
+++ b/src/opticalraytracer/Vector.java
@@ -0,0 +1,173 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.*;
+
+final public class Vector {
+
+	double x = 0, y = 0;
+
+	public Vector(double x, double y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	public Vector(Vector p) {
+		x = p.x;
+		y = p.y;
+	}
+
+	public Vector() {
+	}
+
+	public void assign(Vector v) {
+		x = v.x;
+		y = v.y;
+	}
+
+	public Vector translate(Vector p) {
+		return new Vector(x + p.x, y + p.y);
+	}
+
+	public Vector translate(double px, double py) {
+		return new Vector(x + px, y + py);
+	}
+
+	public Vector translateSub(Vector p) {
+		return new Vector(x - p.x, y - p.y);
+	}
+
+	public Vector scale(double scale) {
+		return new Vector(x * scale, y * scale);
+	}
+
+	public Vector scale(double xs, double ys) {
+		return new Vector(x * xs, y * ys);
+	}
+
+	public Vector rotate(double x, double y, double angleRadians) {
+		double xx = x * cos(angleRadians) - y * sin(angleRadians);
+		double yy = y * cos(angleRadians) + x * sin(angleRadians);
+		return new Vector(xx, yy);
+	}
+
+	public Vector rotate(double angleRadians) {
+		double xx = x * cos(angleRadians) - y * sin(angleRadians);
+		double yy = y * cos(angleRadians) + x * sin(angleRadians);
+		return new Vector(xx, yy);
+	}
+
+	public Vector rotate(Vector v, double angleRadians) {
+		double xx = v.x * cos(angleRadians) - v.y * sin(angleRadians);
+		double yy = v.y * cos(angleRadians) + v.x * sin(angleRadians);
+		return new Vector(xx, yy);
+	}
+
+	public Vector mul(double v) {
+		return new Vector(x * v, y * v);
+	}
+
+	public Vector mul(Vector v) {
+		return new Vector(x * v.x, y * v.y);
+	}
+
+	public Vector div(double v) {
+		return new Vector(x / v, y / v);
+	}
+
+	public Vector div(Vector v) {
+		return new Vector(x / v.x, y / v.y);
+	}
+
+	public Vector add(Vector v) {
+		return new Vector(x + v.x, y + v.y);
+	}
+
+	public Vector add(double v) {
+		return new Vector(x + v, y + v);
+	}
+
+	public Vector sub(Vector v) {
+		return new Vector(x - v.x, y - v.y);
+	}
+
+	public Vector sub(double v) {
+		return new Vector(x - v, y - v);
+	}
+
+	public Vector negate() {
+		return new Vector(-x, -y);
+	}
+
+	public Vector normalize() {
+		// don't try to normalize a zero vector
+		if (x != 0 || y != 0) {
+			double m = m();
+			return new Vector(x/m,y/m);
+		} else {
+			return new Vector(0, 0);
+		}
+	}
+
+	// create a scaled vector from polar coordinates
+
+	static public Vector polar(double m, double a) {
+		return new Vector(m * cos(a), m * sin(a));
+	}
+
+	// create a unit vector from a polar angle
+
+	static public Vector polar(double a) {
+		return new Vector(cos(a), sin(a));
+	}
+
+	public double angle() {
+		return atan2(y, x);
+	}
+
+	public double m() {
+		return sqrt(x * x + y * y);
+	}
+
+	// dot product
+
+	public double dot(Vector v) {
+		return x * v.x + y * v.y;
+	}
+
+	// mark an instance as being in error
+
+	public static Vector invalidState() {
+		return new Vector(Double.NaN, Double.NaN);
+	}
+
+	// test for invalid condition
+
+	public boolean isValid() {
+		return (!Double.isNaN(x) && !Double.isNaN(y));
+	}
+
+	public String toString() {
+		return String.format("{%f,%f}", x, y);
+	}
+
+}
diff --git a/src/opticalraytracer/WavelengthColor.java b/src/opticalraytracer/WavelengthColor.java
new file mode 100644
index 0000000..9cdff35
--- /dev/null
+++ b/src/opticalraytracer/WavelengthColor.java
@@ -0,0 +1,93 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Paul Lutus                                      *
+ *   lutusp at arachnoid.com                                                  *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+package opticalraytracer;
+
+import static java.lang.Math.*;
+
+// This class performs all dispersion-related computations and conversions
+
+final public class WavelengthColor {
+	static final double redNM = 650;
+	static final double violetNM = 400;
+	static final double dispersionPivotNM = 589.3;
+	static final double dispersionFactor = 5e5;
+
+	double r;
+	double g;
+	double b;
+	double wvl;
+	MyColor col;
+
+	public WavelengthColor(double h) {
+		// provide an equivalent wavelength for h
+		wvl = Common.ntrp(h, 0, 1, redNM, violetNM);
+		
+		// adjust to red-violet range in HSV spectrum
+		h *= .75;
+
+		// hToRGB provides a spectrum between red and violet
+		// for arguments between 0 and .75
+		col = hToRGB(h);
+		this.r = col.getRed() / 255.0;
+		this.g = col.getGreen() / 255.0;
+		this.b = col.getBlue() / 255.0;
+	}
+
+	// with hue component of HSV, creates RGB at full saturation
+
+	static  MyColor hToRGB(double h) {
+		double hp = h * 6;
+		double c = 1;
+		double x = c * (1 - abs(hp % 2.0 - 1));
+		MyColor rgb;
+		switch ((int) hp) {
+		case 0:
+			rgb = new MyColor(c, x, 0.0);
+			break;
+		case 1:
+			rgb = new MyColor(x, c, 0.0);
+			break;
+		case 2:
+			rgb = new MyColor(0.0, c, x);
+			break;
+		case 3:
+			rgb = new MyColor(0.0, x, c);
+			break;
+		case 4:
+			rgb = new MyColor(x, 0.0, c);
+			break;
+		case 5:
+			rgb = new MyColor(c, 0.0, x);
+			break;
+		default:
+			rgb = new MyColor(0.0, 0.0, 0.0);
+			break;
+		}
+		return rgb;
+	}
+	
+	// dior = ior + ((dp - w) * 5e5) / (abbe * dp * w^2)
+	static double dispersionIndex(double ior, double wavelength, double abbe) {
+		return ior + ((dispersionPivotNM - wavelength) * dispersionFactor)
+				/ (abbe * dispersionPivotNM * wavelength * wavelength);
+	}
+
+}
diff --git a/src/opticalraytracer/applet_test_page.html b/src/opticalraytracer/applet_test_page.html
deleted file mode 100644
index b0614ea..0000000
--- a/src/opticalraytracer/applet_test_page.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<html>
-<body>
-    
-    <div style="width:875px;height:650px;padding:3px;background:#0000c0;">
-              <applet code="opticalraytracer/OpticalRayTracerApplet.class" archive="../../dist/OpticalRayTracer.jar" width="875" height="650" mayscript>
-              </applet>
-            </div>
-    <form name="clipform">
-        <p>Copy/Paste Area:</p>
-        <p><textarea name="cliparea" style="width:875;height:200;">
-        </textarea></p>
-</form>
-</body>
-</html> 
diff --git a/src/opticalraytracer/help_resources/HelpText.html b/src/opticalraytracer/help_resources/HelpText.html
deleted file mode 100644
index 81d0aa7..0000000
--- a/src/opticalraytracer/help_resources/HelpText.html
+++ /dev/null
@@ -1,644 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>
-      * OpticalRayTracer
-    </title>
-    <style type="text/css">
-td {
-  border-width:1px;
-  border-style:solid;
-  border-color:black;
-  border-collapse:collapse;
-  /* white-space:nowrap; */
-}
-.article_subtopic {
-  font-weight:bold;
-}
-    </style>
-  </head>
-  <body bgcolor="#ffffff" text="#000000" link="#0000ff" vlink="#800080" alink="#ff0000">
-    
-    <div align="center">
-      <p><h2><img src="../icons/OpticalRayTracer.png" width="32" height="32" title="" alt=""/> OpticalRayTracer #version# Help Page</h2></p>
-      
-      
-      <p><i>A virtual lens design workshop.</i></p>
-      <p>Copyright © 2011, <a href="http://arachnoid.com/administration/index.html" title="Click for biography">Paul Lutus</a> — <a href="http://arachnoid.com/messages/index.php">Message Page</a></p>
-      
-      <p>OpticalRayTracer is released under the GPL: <a href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a></p>
-      
-      <p>Visit the <a href="http://arachnoid.com/OpticalRayTracer">OpticalRayTracer Home Page</a> for more information and to be sure you have the latest version.</p>
-      
-      
-      <p><b>NOTE:</b><i> For formatting reasons, users may want to temporarily make the OpticalRayTracer program frame larger to properly read these instructions.</i></p>
-      
-      <p><b>NOTE:</b><i> Users may prefer to search this document using the search feature at the bottom of this frame.</i></p>
-      
-      <p><!-- LINK_MENU_START -->
-<a href="#Introduction">Introduction</a> | <a href="#First_Steps">First Steps</a> | <a href="#The_Basics_of_Lenses">The Basics of Lenses</a><br/>
-<a href="#Supported_Lens_Types">Supported Lens Types</a> | <a href="#Tutorial">Tutorial</a> | <a href="#Lens_Control_Panel">Lens Control Panel</a><br/>
-<a href="#Dispersion_Experiment">Dispersion Experiment</a> | <a href="#Using_the_Mouse_and_Keyboard">Using the Mouse and Keyboard</a> | <a href="#Importing_and_Exporting_Data">Importing and Exporting Data</a><br/>
-<a href="#System_Considerations">System Considerations</a> | <a href="#Algorithm_Description">Algorithm Description</a> | <a href="#Snell_s_Law">Snell's Law</a><br/>
-<a href="#Dispersion_Computation">Dispersion Computation</a> | <a href="#Configuration">Configuration</a> | <a href="#Conclusion">Conclusion</a><br/>
-<a href="#User_support">User support</a>
-<!-- LINK_MENU_END --></p>
-      
-      
-      
-      
-    </div>
-    <p></p>
-    
-    <a name="Introduction"></a><div class="article_subtopic">Introduction</div>
-    
-    
-    
-    
-    <blockquote>
-      <p>OpticalRayTracer is a very portable Java program meant to analyze and model systems of lenses. It accurately models the physics of lenses, including the effect known as dispersion. But perhaps the most remarkable thing about OpticalRayTracer is that it updates and displays complex ray tracing paths in real time, as the user moves virtual lenses around on a virtual optical bench. This allows the user to very quickly learn the behavior of a system of lenses, compare, experiment, a [...]
-      
-      <p>OpticalRayTracer places its configuration file in a directory it creates, so your settings and choices are preserved. This directory is located at #userdir# on your machine, and it contains a configuration file named "OpticalRayTracer.ini" containing quite a lot of detailed information unique to your use of the program.</p>
-      
-      
-      
-      <p>I mention this because:</p>
-      
-       <ul>
-  <li>if you want to analyze or process the results of your work with OpticalRayTracer, the file #userdir#/OpticalRayTracer.ini contains a lot of numeric information in plain-text form, and</li>
-          <li>if you have gotten into difficulty and just want to start over, simply delete this file and run OpticalRayTracer again.</li>
-        </ul>
-      
-      
-      
-      <p>This file contains a very detailed snapshot of your last session with OpticalRayTracer, with lens specifications and positions, suitable for exporting into other environments (the same information can be gotten from the <img src="../icons/edit-copy.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> copy-configuration button on the Design toolbar). To create this file and its picture of your optical setup, simply exit OpticalRayTracer, navigate to #userd [...]
-      
-      
-    </blockquote>
-    
-    <a name="First_Steps"></a><div class="article_subtopic">First Steps</div>
-    
-    <blockquote>
-      
-      <p>Since you are reading this, you have successfully installed OpticalRayTracer, and are ready to try some experiments.</p>
-      
-      <p>When it is first run, the program will automatically create two common lenses for you, a double-convex lens and a double-concave lens. Click the <img src="../icons/applications-graphics.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Design" tab and you will most likely see these two default lenses. If you do not see any lenses, click the <img src="../icons/process-stop.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Er [...]
-      
-      <p>Navigation within the ray trace display is as intuitive as I could make it:</p>
-      
-      <ul>
-        <li>To zoom in and out, use your mouse wheel.</li>
-        
-        <li>To pan around the display, just click the display and drag using the mouse.</li>
-        
-        <li>To move a lens from one place to another in the display, simply click the lens and drag it.</li>
-      </ul>
-      
-      <p>Notice that panning the display itself requires you to click outside any lenses, then drag. To move a lens, click directly on the lens you want to move and drag it.</p>
-      
-      <ul>
-  <li>The display has two dimensions, "x" and "y". "x" is the horizontal dimension, and positive values for x move to the right. "y" is the vertical dimension, and positive values for y move up.</li>
-        
-        <li>To move large distances, first zoom out, then pan, then zoom in again. This saves time compared to clicking and dragging multiple times.</li>
-        
-        <li>To determine the position of something in the display, for example the location of a focal point, simply point the mouse cursor at the point of interest and read the mouse x and y position on the status bar.</li>
-      </ul>
-      
-      <p>The default setup shows six light beams passing from right to left, through the lenses. The mathematical methods used in this program are efficient enough that (with a moderately fast computer) you can move the lenses around and see how this changes the beam paths — in real time. Try it — move the lenses around (click on a lens and drag it) and observe the changing beam paths.</p>
-      
-      <p>Notice that, when you click a lens, a selection frame appears and the blue control panel below the display becomes enabled. This panel allows you to change the characteristics of your lenses — focal length, size, curvature, and many other things. Feel free to experiment with this panel's settings — see how they change the appearance of the lenses and the beams.</p>
-      
-      <ul>
-  <li>If you make a change you don't like, simply press the <img src="../icons/edit-undo.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Undo" button to change back.</li>
-        
-        <li>To change the settings for a lens, first click the lens, then use the lens control panel to make the changes you want.</li>
-      </ul>
-      
-      <p>It will help to know a little about optics to understand what you are seeing. If you already know the basics, you can safely skip the next few lines.</p>
-      
-    </blockquote>
-    <a name="The_Basics_of_Lenses"></a><div class="article_subtopic">The Basics of Lenses</div>
-    
-    <blockquote>
-      
-      <p>Very basically, a lens is a simple way to bend light beams. Imagine a row of soldiers marching, side by side. To change the direction they are marching, it is necessary to make some of the soldiers slow down temporarily. Now let this picture of marching soldiers help you imagine a light wave, traveling through space. Just as with the soldiers, to make the wave change direction, you have to think of a way to make part of the wave slow down. That is what a lens does — it selective [...]
-      
-      <p>A convex lens is thicker in the middle than at the edges, and, as it turns out, light takes longer to pass through glass than through air. What this means is that the light that passes through the middle, thickest part of the lens, is slowed down compared to the light that passes through the thinner parts near the edges of the lens. This has the effect of shaping the wavefront that emerges from the lens — the middle of the emerging wavefront is delayed, and the wave's overall sh [...]
-      
-      <p>Such a lens could be used to focus parts of a real-world scene onto a piece of film or an image sensor. The ability of a lens to focus accurately is a central issue in lens design and, as it turns out, the most common kind of lens, with a spherical shape, is actually quite a bad lens. Its only advantage is that it is easy to make — everything after that is downhill.</p>
-      
-      
-    </blockquote>
-    
-    <a name="Supported_Lens_Types"></a><div class="article_subtopic">Supported Lens Types</div>
-    
-    <blockquote>
-      
-      
-      <p>OpticalRayTracer will happily let you play with various kinds of simple, spherical lenses in its virtual playground, but it also includes some mathematical methods that allow you to fashion some rather extraordinarily good lenses called "hyperboloids," famous for their accuracy ... and their difficulty of manufacture. These kinds of lenses are so expensive that it is simpler — and much less expensive — to build and test such lenses using a program like OpticalRayTracer than to t [...]
-      
-      
-    </blockquote>
-    
-    <a name="Tutorial"></a><div class="article_subtopic">Tutorial</div>
-    
-    <blockquote>
-      
-      <p>I want you to perform your own experiments, but here's a simple tutorial to get you started. Using the default lenses automatically created when you run OpticalRayTracer the first time, temporarily drag the concave lens (the lens at the right) out of the optical path. If you drag it a small distance, it will jump back into place, realigning itself with the beam line (ordinarily this automatic feature is a good thing). So drag it a good distance up or down, temporarily removing i [...]
-      
-      <ul>
-        <li>"Lens Radius" refers to the distance from the center to the edge of the lens itself. Very simple.</li>
-        
-        <li>"Sphere radius" refers to imaginary spheres that our lens is composed of. Imagine two spheres of glass floating in space, overlapping to some extent, and then imagine that our lens represents the parts of the spheres that overlap. To help in visualizing this, draw two circles on a piece of paper using a coin and a pencil, and be sure to allow some overlap between the circles. The area where the circles overlap is our lens. Starting with this paper diagram, one need only use i [...]
-      </ul>
-      
-      <p>As it turns out, the mathematics behind lenses relies very much on this idea of overlapping spheres, hyperboloids, and some other useful shapes. So if you can mentally picture two overlapping spheres, you will be able to predict what will result from your typing particular numbers into OpticalRayTracer. For example, to create a lens with one side convex and one side flat, you might choose to enter a very, very large radius for one side. Like this:</p>
-      
-      <ul>
-        <li>Select the double-convex lens by clicking it.</li>
-        
-        <li>Deselect the "symmetrical" check box, which will allow you to choose different traits for the left and right-hand sides of your lens.</li>
-        
-        <li>Enter "1000" into the "Left Sphere Radius" window and press Enter.</li>
-        
-        <li>You will see that the right-hand side of the lens has become flat, and (because we now have a lens with less overall curvature), the beams travel farther to the right before converging. The lens is said to have a longer focal length.</li>
-      </ul>
-      
-      <p>At this point, you may wonder why an entry defining the left-hand sphere had its effect on the right hand side of our lens. The answer is that the imaginary spheres are overlapping, and the <i>right-hand</i> part of our lens is defined by an imaginary sphere centered to the <i>left</i> of the lens. I mention this now to avoid confusion later on. The circle that defines the right-hand side of the overlapped region (e.g. the lens) is centered to the left of the overlap area.</p>
-      
-      <p>Now for something a tiny bit more advanced.</p>
-      
-      <ul>
-        <li>Select the default convex lens by clicking on it.</li>
-        
-        <li>Re-enter "6" for "Left Sphere Radius," press Enter, then enable "symmetrical" again by clicking its checkbox, restoring our lens to its original state — a simple, symmetrical double-convex lens.</li>
-        
-        <li>Drag the mouse on the display to pan over to the region between x = 5 and 6 (remember that "x" means the horizontal axis), where the beams should now be converging. Once you have centered this part of the display, use the mouse wheel to zoom in a bit for a closer look.</li>
-        
-        <li>While looking as the point where the beams converge, click the "Left Hyperboloid" checkbox (because you selected "symmetrical" above, this entry will affect both sides of our lens). The lens focus should improve.</li>
-        
-        <li>A quick reality check — what constitutes an "improvement" in a lens? Well, ideally, all the beams should converge on a single point, rather than taking slightly different paths as they are doing now.</li>
-        
-        <li>Now we are going to fine-tune our lens by entering a value for hyperboloid curvature. Type ".074" into the "Left Curv. Fact." window, and press Enter. If you have entered all the right values up to now, this should produce a nearly perfect focus — all the beams should converge on a single point, located at about x = 4.996.</li>
-      </ul>
-      
-      <p>To discover how accurate this focus is, simply center the focal point in the display and zoom in on it. Eventually you will get to a point where you can see some small imperfections in the focus. (This will become visible at a zoom factor of about 15.) In any case, this class of lens design is very advanced and (no surprise) very difficult to manufacture.</p>
-      
-    </blockquote>
-    
-    <a name="Lens_Control_Panel"></a><div class="article_subtopic">Lens Control Panel</div>
-    
-    <blockquote>
-      
-      <p>Play with some of the settings in the lens control panel (the blue panel below the graphic display) to see what effect they have. Notice that you can reposition a lens exactly by entering its x and y coordinates — this is a way to get around the fact that it is difficult to position a lens precisely using the mouse.</p>
-      
-      <p>Notice the window marked "IOR". This means "Index of Refraction," a value representing the ratio of the speed of light through the lens in question to a vacuum (with an IOR of 1.0). If you set this value to 1.0, the lens will no longer deflect the light beams, because it has in essence been defined as empty space.</p>
-      
-      <p>Different glasses have different indices of refraction, a property that is taken advantage of in advanced lens designs. Here's an example design that exploits this fact, and introduces the topic of dispersion.</p>
-      
-      <p>"Dispersion" is a property of glass in which light beams of different wavelengths travel at different speeds. For example, a blue beam takes longer to move through a lens than a red beam. This causes the two colors (wavelengths) of light to focus at two different places, a trait regarded as a bad thing, called "chromatic aberration."</p>
-      
-    </blockquote>
-    
-    <a name="Dispersion_Experiment"></a><div class="article_subtopic">Dispersion Experiment</div>
-    
-    <blockquote>
-      
-      <p>To set up for this experiment, delete any existing lenses, then create a lens with these settings (or you can copy the lens definition from this page — see below):</p>
-      
-      <ul>
-        <li>Symmetrical: selected.</li>
-        <li>Lens Radius: 2.0</li>
-        <li>Left Sphere Radius: 4.0</li>
-        <li>Right Sphere Radius: 4.0</li>
-        <li>Lens Thickness: 0.0</li>
-        <li>Index of Refraction: 1.52</li>
-        <li>Dispersion: 59</li>
-        <li>X position: 0.0</li>
-        <li>Y position: 0.0</li>
-      </ul>
-      
-      <p><a href="file:'0.0|0.0|2.0|4.0|4.0|0.0|1.52|0.03|0.03|59.0|false|false|true'">Click here</a> to copy this lens definition onto the clipboard, then paste it into the experimental setup using the display context menu (use "Paste: defined position").</p>
-      
-      <p>Now switch to the "Configuration" panel and enter 2 for "Beam Count". Now return to the ray trace display to see the effect.</p>
-      
-      <p>If all your settings are correct, and if the lens has really been positioned at x = 0, y = 0, the two beams should converge at about x = 2.53.</p>
-      
-      <p>Now we'll add a dispersion calculation. Go to the "Configuration" panel and enter 8 for "Dispersion beam count." When you return to the ray trace display, you should see an array of colored beams near the lens focal point. In this mode, OpticalRayTracer creates colored beams, each of which has an associated wavelength. During the calculation of the ray paths, the lens dispersion property is taken into account and, just as in the real world, the lens cannot focus all these wavele [...]
-      
-      <p>Moving right along, create a second lens (or copy it from this page — see below) with these properties:</p>
-      
-      <ul>
-        <li>Symmetrical: deselected.</li>
-        <li>Lens Radius: 2.0</li>
-        <li>Left Sphere Radius: 1000</li>
-        <li>Right Sphere radius: -4</li>
-        <li>Lens Thickness: 0.1</li>
-        <li>Index of Refraction: 1.72</li>
-        <li>Dispersion: 29</li>
-        <li>X position: 0.3834</li>
-        <li>Y position: 0.0</li>
-      </ul>
-      
-      <p><a href="file:'0.3834|0.0|2.0|1000.0|-4.0|0.1|1.72|1.0E-8|1.0E-8|29.0|false|false|false'">Click here</a> to copy this lens definition onto the clipboard, then paste it into the experimental setup using the display context menu (use "Paste: defined position").</p>
-      
-      <p>If all the settings on both lenses are correct and all the other required settings have been made correctly, you will see all the colored beams converge at about x = 11.63, with very little color dispersion.</p>
-      
-      <p>This, by the way, is a classic solution to the problem of chromatic aberration, using varieties of glass called "crown" and "flint," with differing properties that are exploited to make the light beams converge.</p>
-      
-      <p>By changing the spacing between the two lenses, you will quickly see that this setting is very critical to the outcome, which is why in the real world, such pairs of lenses are often glued together or placed in a lens cell with a spacer of some durable material to maintain a particular separation.</p>
-      
-    </blockquote>
-    
-    <a name="Using_the_Mouse_and_Keyboard"></a><div class="article_subtopic">Using the Mouse and Keyboard</div>
-    
-    <blockquote>
-      
-      <p>While playing with lens configurations, you may sometimes notice it is difficult to select a particular lens, because the lenses are very close together and their colored border-boxes overlap. In a case like this, just click again — the program will cycle through the lenses that could be selected at the location of your click.</p>
-      
-      <p>Virtually all OpticalRayTracer's text entry fields can be changed by placing the mouse cursor over them and spinning the mouse wheel. If the rate of change is too fast, hold down the shift key while spinning the mouse. If that rate is also too fast, hold down the shift and Alt keys together while spinning the mouse.</p>
-
-
-      
-      <p>
-        These actions can be gotten with some special keyboard keys also — the up and down arrow keys will change the value by +1 and -1 respectively, with smaller changes if the shift and/or Alt keys are held down, just as with the mouse wheel example above. Here is a full list of these special controls:
-      </p>
-      
-      <blockquote>
-        <table cellspacing="-1" cellpadding="4" class="bordered" style="background:#fbfceb;">
-          
-          <tr style="background:#b2e0b4;">
-            <td>
-              <div align="center"><b>Action</b></div>
-            </td>
-            <td>
-              <div align="center"><b>Result</b></div>
-            </td>
-          </tr>
-          
-          <tr>
-            <td>
-              Mouse wheel
-            </td>
-            <td>
-              Value increased/decreased by 1
-            </td>
-          </tr>
-          
-          <tr>
-            <td>
-              up/down arrow keys
-            </td>
-            <td>
-              Value increased/decreased by 1
-            </td>
-          </tr>
-          
-          <tr>
-            <td>
-              Page Up/Page Down keys
-            </td>
-            
-            <td>
-              Value increased/decreased by 10
-            </td>
-          </tr>
-          <tr>
-            <td>
-              Home/End keys
-            </td>
-            <td>
-              Value increased/decreased by 100
-            </td>
-            
-          </tr>
-        </table>
-        
-      </blockquote>
-      
-      <p>The following actions apply to the graphic display panels:</p>
-      
-      <blockquote>
-        <table cellspacing="-1" cellpadding="4" class="bordered" style="background:#fbfceb;">
-          <tr style="background:#b2e0b4;">
-            <td>
-              
-              <div align="center"><b>Action</b></div>
-            </td>
-            <td>
-              <div align="center"><b>Result</b></div>
-            </td>
-          </tr>
-          <tr>
-            <td>
-              
-              Mouse wheel
-            </td>
-            <td>
-              Zoom in/out
-            </td>
-          </tr>
-          <tr>
-            <td>
-              Mouse drag
-            </td>
-            
-            <td>
-              Pan image vertically or horizontally
-            </td>
-          </tr>
-        </table>
-      </blockquote>
-      
-      <p>Most of the above mouse and keyboard actions can be changed with the following modifier keystrokes:
-</p>
-
-<blockquote>
-        <table cellspacing="-1" cellpadding="4" class="bordered" style="background:#fbfceb;">
-          <tr style="background:#b2e0b4;">
-            <td>
-              
-              <div align="center"><b>Action</b></div>
-            </td>
-            <td>
-              <div align="center"><b>Result</b></div>
-            </td>
-          </tr>
-          <tr>
-            <td>
-              Above actions accompanied by Shift key
-            </td>
-            <td>
-              Amount of change divided by 10
-            </td>
-          </tr>
-
-<tr>
-            <td>
-              Above actions accompanied by Alt key
-            </td>
-            <td>
-              Amount of change divided by 100
-            </td>
-          </tr>          
-          <tr>
-            <td>
-              Above actions accompanied by both Shift and Alt keys
-            </td>
-            <td>
-              Amount of change divided by 1000
-            </td>
-          </tr>
-        </table>
-        
-      </blockquote>      
-      
-      
-      
-    </blockquote>
-    
-    
-    
-    <a name="Importing_and_Exporting_Data"></a><div class="article_subtopic">Importing and Exporting Data</div>
-    
-    
-    <blockquote>
-    
-    <p>OpticalRayTracer has a number of methods for writing and reading data to/from the world at large, primarily by way of the system clipboard.</p>
-    
-    <ul>
-  <li>To create a copy of a lens you have designed, simply click the lens, press the right mouse button and choose <img src="../icons/edit-copy.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy".</li>
-      
-      <li>To paste that lens somewhere else, move the mouse cursor to the desired destination point, press the right mouse button and select <img src="../icons/edit-paste.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Paste: mouse cursor."</li>
-  
-  <li>You can save lens descriptions in other locations, or even paste them into an e-mail for a friend, by simply pasting the lens description from the system clipboard. This works because an OpticalRayTracer lens description is a plain-text line of ordinary characters like this:
-  
-  <blockquote>
-0.0|0.0|2.0|6.0|6.0|0.0|1.52|0.03|0.03|59.0|false|false|true
-</blockquote>
-  </li>
-</ul>
-
-<p>You can also make a copy of the entire experimental setup — lenses, colors, zoom levels, everything — by clicking the "Copy" button on the toolbar below the graphic display (not the context-menu copy button). This places a full description of OpticalRayTracer's present state onto the system clipboard. And this exact state can be reëstablished by pasting such a description using the toolbar "Paste" button. This means you can send a full, exact description of your experimental setu [...]
-
-To make a graphic copy of the workspace display, click the <img src="../icons/applications-multimedia.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy Workspace" button, then open a graphic image editor and choose "Paste".
-
-      
-    </blockquote>
-    
-    <a name="System_Considerations"></a><div class="article_subtopic">System Considerations</div>
-    
-    <blockquote>
-    
-    <p>If the behavior of your copy of OpticalRayTracer doesn't correspond in every way to the description provided here, it probably means your installed version of Java is not up-to-date. To remedy this, visit <a href="http://java.com">http://java.com</a> to update your Java installation (Java is free).</p>
-    
-      <p>Remember that the total number of beams traced is equal to the number of tracing beams (selected in the "Configuration" panel) multiplied by the number of dispersion beams, e. g. there is a dispersion beam for each chosen wavelength, times each tracing beam. So if the display slows down, this could easily be the reason — too many beams selected. To prevent calculation of dispersion, simply set "Number of Dispersion Beams" to zero.</p>
-      
-      <p>OpticalRayTracer uses several mathematical methods to produce its results (and you got a copy of all the source files when you downloaded the program, so you can examine the methods in detail).</p>
-      
-      </blockquote>
-    
-    <a name="Algorithm_Description"></a><div class="article_subtopic">Algorithm Description</div>
-    
-    <blockquote>
-    
-    (For a more complete presentation of this topic, visit the <a href="http://arachnoid.com/OpticalRayTracer/documentation.html">OpticalRayTracer technical discussion page</a>.)
-    
-      <p>OpticalRayTracer first calculates the location of any intersections between tracing beams and spheres or hyperboloids (our lenses). The collision detection mathematics is rather involved and won't be described in any detail here.</p>
-      
-      <p>Having acquired a list of all possible points of collision for a particular beam, OpticalRayTracer sorts the list of results along the x dimension, then determines which intersection is next (to the right) along the beam's path.</p>
-      
-      <p>At this point OpticalRayTracer has determined a point of collision between a tracing ray and a lens. The ray and the lens collision point each have a characteristic angle, which is used in the next computation.</p>
-      
-    </blockquote>
-    
-    <a name="Snell_s_Law"></a><div class="article_subtopic">Snell's Law</div>
-    
-    <blockquote>
-      <p>"Snell's Law" is a classic optical relationship that, given arguments for incidence angle between two media and indices of refraction for the two media, determines the deflection angle. Expressed in classic form, Snell's Law is:</p>
-      
-      <blockquote>
-        n1 sin(a1) = n2 sin(a2)
-      </blockquote>
-      
-      <p>Where:</p>
-      
-      <ul>
-        <li>n1 = index of refraction of medium 1</li>
-        <li>a1 = angle within medium 1</li>
-        
-        <li>n2 = index of refraction of medium 2</li>
-        <li>a2 = angle within medium 2</li>
-      </ul>
-      
-      <p>The astute reader will notice that, in passing from a medium like air with an IOR near 1.0, to a lens with an IOR of 1.5 for example, the angle must decrease. And conversely, a beam emerging from glass to air will show an increase in its angle of deflection. Also, it can be seen that an incident angle of zero will not be deflected — it will remain zero.</p>
-      
-      
-      
-      <p>In computing refraction, OpticalRayTracer uses this restatement of the Snell's Law equation:</p>
-      
-      
-      
-      <blockquote>
-        a2 = arcsin(n1 sin(a1) / n2)
-      </blockquote>
-      
-      <p>Here is a practical example:</p>
-      
-      
-      
-      <ul>
-        <li>Medium 1: air
-          
-          <ul>
-            <li>n1 = 1.0</li>
-            <li>a1 = 20 degrees</li>
-          </ul>
-        </li>
-        <li>Medium 2: crown spectacle glass
-          
-          <ul>
-            <li>n2 = 1.52</li>
-            <li>a2 = arcsin(1.0 sin(a1) / 1.52) = 13.00365 degrees</li>
-          </ul>
-        </li>
-      </ul>
-    </blockquote>
-    
-    <a name="Dispersion_Computation"></a><div class="article_subtopic">Dispersion Computation</div>
-    
-    <blockquote>
-      <p>The computation for dispersion follows along similar lines, but with an empirical equation less grounded in physically simple principles (and original with the author). The dispersion equation changes the index of refraction based on the wavelength of the light beam:</p>
-      
-      
-      
-      <blockquote>
-        ior2 = ior + ((dp - w) * 500000.0) / (abbe * dp * w^2)
-      </blockquote>
-      
-      <p>Where:</p>
-      
-      
-      
-      <ul>
-        <li>ior = original index of refraction for the medium.</li>
-        
-        <li>dp = dispersion pivot wavelength, set to 589.3 nm, the sodium yellow line.</li>
-        
-        <li>w = wavelength of the tracing beam in nm (nanometers).</li>
-        <li>abbe = Abbe's Number, a value associated with many glasses and that describes its dispersion property. Lower Abbe's numbers result in higher dispersion.</li>
-      </ul>
-      
-      
-      
-      <p>Abbe's Number is arrived at in this way:</p>
-      
-      
-      
-      <blockquote>
-        abbe = (nd-1)/(nf-nc)
-      </blockquote>
-      
-      <p>Where:</p>
-      
-      
-      
-      <ul>
-        <li>nf = a medium's index of refraction at the 486.1 nm hydrogen blue line.</li>
-        <li>nd = a medium's index of refraction at the 589.3 nm sodium yellow line.</li>
-        <li>nc = a medium's index of refraction at the 656.3 nm hydrogen red line.</li>
-      </ul>
-      
-      <p>The Abbe numbers for various media are arrived at in laboratory experiments. My equation simply reverses the relationship between the number and its effects, within an accuracy of about 1%.</p>
-      
-      
-    </blockquote>
-    
-    <a name="Configuration"></a><div class="article_subtopic">Configuration</div>
-    
-    <blockquote>
-      <ul>
-   <li>To change a color in the "Configure" panel's selection list, simply click the colored button for the value you want to change. A color selection dialog will appear.</li>
-        
-       <li>All the configuration values, along with a full description of the lenses you create, are preserved between sessions in a file located at #userdir#/OpticalRayTracer.ini on this system, and the same information can be copied onto the system clipboard with the toolbar "Copy Configuration" button.</li>
-     </ul>
-      
-      <p>Here is an explanation of the controls in the Configuration panel:</p>
-      
-      <ul>
-      <li><b>Domain Barrier Color</b>. This is the color of the left and right-hand barriers that define the experimental domain.</li>
-      
-        <li><b>Y Baseline Color</b>. This is the color of the reference line at y = 0 in the ray trace display. If you do not want this line to appear, set its color to the color of the display background (see "Display Background" below).</li>
-        
-        <li><b>Grid Color</b>. This is the auto-scaling grid in the ray trace display. Again, you can turn this off by setting its color to that of the background.</li>
-        
-        <li><b>Lens Outline Color</b>. This color is used to draw the profile of each lens.</li>
-        
-        <li><b>Tracing Beams Color</b>. This is the beam color that is used when dispersion is not being computed. Remember that dispersion beams have an internally computed color, appropriate to their wavelength.</li>
-        
-        <li><b>Beam Intersection Color</b>. This is the color of the dots that mark the intersections between light beams and lens surfaces.</li>
-        
-        <li><b>Lens Selected Box Color</b>. This color might be better allowed to remain in view, since it tells you which lens has been selected and to which lens the control panel settings apply.</li>
-        
-        <li><b>Display High Background Color</b>. This color defaults to white, but in some cases, like trying to make out the color of dispersion beams, another color might be better.</li>
-        
-        <li><b>Display Low Background Color</b>. This color defaults to black, but the user has the option of choosing a different color.</li>
-        
-        <li><b>Intersection Dot Size</b>. This numeric entry specifies the size of the dots that mark intersections between light beams and surfaces. A positive entry produces circles, a negative entry produces solid disks of color.</li>
-        
-        <li><b>Y Snap-to-Base Band</b>. This numeric entry sets the threshold for the behavior that returns a lens to the Y baseline when the mouse is released. This is ordinarily a good thing, a feature, but if you prefer this not to happen, set this number to zero. In truth, the feature is always active regardless of the this setting, but if you enter zero, the feature is only active at a threshold value of zero, which ... oh well, I think you get the idea.</li>
-        
-        <li><b>Beam Width</b>. This setting actually sets the width of all image lines. An entry of zero is ignored, the minimum beam width is 1.</li>
-        
-        <li><b>Beam Count</b>. This is a setting with an large impact on program performance. OpticalRayTracer's algorithms are swift, but choosing a large number of tracing beams might disable one of its best features: real-time response to user inputs. Also, it is easy to become overloaded with information as the number of beams increases. It is difficult to take advantage of the information presented by more than about 8 tracing beams. Lots of beams can be entertaining, though.</li>
-        
-        <li><b>Interactions per beam</b>. This determines the limit to interactions for a given beam, to avoid the computation of pointless internal reflections within a lens, for example. The default value is quite large, but a sufficiently complex lens system might require that it be increased.</li>
-        
-        <li><b>Source Y Start</b> and</li>
-        <li><b>Source Y End</b>. These values determine the vertical limits for the array of tracing beams. These two numbers are typically set to fall within the chosen radius of your lenses. The default settings of -1.8 and 1.8 means all the beams fall comfortably within the default lens radius of 2 units (remember that a lens diameter is twice its radius).</li>
-        
-        <li><b>X Source Plane</b>. This is the location in the x dimension from which the tracing beams emanate. In the ray trace display, zoom out to see the location of the beam origin, then change this number to see what happens.</li>
-        
-        <li><b>X Target Plane</b>. This is the plane in the x dimension through which the tracing beams pass, regardless of their angle (described below). It is useful as a movable reference point, for example to see how a focusing effort is turning out.</li>
-        
-        <li><b>Beam Offset Angle</b>. This setting is used to tilt the array of tracing beams, a way to test the off-axis performance of a lens without having to rotate the lens itself. By controlling the source angle you can observe the resulting path through a series of lenses, thus determining the off-axis performance for the entire system.</li>
-        
-        <li><b>Dispersion Beam Count</b>. This setting produces a set of dispersion beams, with appropriate colors and wavelengths, for each tracing beam. These dispersion beams will be deflected by different amounts depending on specific lens settings, in particular the dispersion value. This is the key to the dispersion effect simulation.</li>
-        
-        <li><b>Rotate from X Origin</b>. When using a nonzero beam offset angle, this checkbox pivots the beams with respect to X = 0 rather than the X target plane. This allows light beams to be rotated around a lens located at X = 0.</li>
-        
-        <li><b>Diverging Beams</b>. This checkbox causes the tracing beams to originate in and diverge from a point source located at the X source reference plane, rather than being generated in parallel.</li>
-        
-      </ul>
-      
-      <p>Remember that the total number of ray trace computations is equal to the number of tracing beams multiplied by the number of dispersion beams, such that choosing 8 tracing beams and 8 dispersion beams results in 64 traces, fine for a fast computer, but not so great for a slower machine. To prevent the generation of dispersion beams and their associated computation overhead, simply set this value to zero.</p>
-      
-      
-    </blockquote>
-    
-    <a name="Conclusion"></a><div class="article_subtopic">Conclusion</div>
-    
-    <blockquote>
-      <p>The OpticalRayTracer Home Page is located at <a href="http://www.arachnoid.com/OpticalRayTracer">http://www.arachnoid.com/OpticalRayTracer</a>, where additional documentation and other resources are located. Be sure to visit to make sure you have the latest version of OpticalRayTracer.</p>
-      
-      
-      
-      <p>There is a great deal of excellent, detailed information about optics on the Web, both theoretical and practical. Google for "optics," "ray tracing" and related topics.</p>
-      
-      
-    </blockquote>
-    
-    <a name="User_support"></a><div class="article_subtopic">User support</div>
-    
-    <blockquote>
-      
-      <p>There is a more detailed technical description of OpticalRayTracer and optical mathematics in general located at <a href="http://arachnoid.com/OpticalRayTracer/documentation.html">http://arachnoid.com/OpticalRayTracer/documentation.html</a></p>
-      
-      <p>Because OpticalRayTracer is released under the <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> (but please visit <a href="http://www.arachnoid.com/careware">http://www.arachnoid.com/careware</a> anyway), there is no user support. This help file plus the sort of knowledge available in optical textbooks and online should be sufficient to help the user make it productive.</p>
-      
-      <p>If you detect a bug in OpticalRayTracer, please report it at <a href="http://www.arachnoid.com/messages">http://www.arachnoid.com/messages</a>.</p>
-      
-      
-    </blockquote>
-  </body>
-</html>
diff --git a/src/opticalraytracer/help_resources/HelpText.txt b/src/opticalraytracer/help_resources/HelpText.txt
deleted file mode 100644
index f88d45a..0000000
--- a/src/opticalraytracer/help_resources/HelpText.txt
+++ /dev/null
@@ -1,285 +0,0 @@
-*** Help for OpticalRayTracer
-
-OpticalRayTracer is (c) Copyright 2008, P. Lutus.
-
-OpticalRayTracer is released under the GPL (the GNU General Public License). Additionally, it is CareWare (no money, now or ever). If you are curious, visit the CareWare Page at www.arachnoid.com/careware.
-
-For recent revisions and further information, visit the OpticalRayTracer Home Page at http://www.arachnoid.com/OpticalRayTracer.
-
-*** Introduction
-
-OpticalRayTracer is a Linux-specific C++ program meant to analyze and model systems of lenses. It accurately models the physics of lenses, including the effect known as dispersion. But perhaps the most remarkable thing about OpticalRayTracer is that it updates and displays complex ray tracing paths in real time, as the user moves virtual lenses around on a virtual optical bench. This allows the user to very quickly learn the behavior of a system of lenses, compare, experiment, and just play.
-
-OpticalRayTracer places its configuration file in a directory it creates, so your settings and choices are preserved. This directory is located at [PROGDIR] on your machine, and it contains a configuration file named "OpticalRayTracer.ini" containing quite a lot of detailed information unique to your use of the program.
-
-I mention this because (a) if you want to analyze or process the results of your work with OpticalRayTracer, the file [PROGDIR]/OpticalRayTracer.ini contains a lot of numeric information in plain-text form, and (b) if you have gotten into difficulty and just want to start over, simply delete this file and run OpticalRayTracer again.
-
-This file contains a very detailed snapshot of your last session with OpticalRayTracer, with lens specifications and positions, suitable for exporting into other environments. To create this file and its picture of your optical setup, simply exit OpticalRaytracer, navigate to [PROGDIR] and read the file.
-
-*** First Steps
-
-Since you are reading this, you have successfully installed OpticalRayTracer on your Linux system, and are ready to try some experiments. If you want to follow these help instructions closely while using the program, you may want to consider making a copy of this help file. To do this, simply click the help display, then press Ctrl+A (select all), then Ctrl+C (Copy). Then paste the text into your favorite text editor/word processor for reading in a separate window, or printing.
-
-When it is first run, the program will automatically create two common lenses for you, a double-convex lens and a double-concave lens. Click the "Ray Trace Display" tab and you will most likely see these two default lenses. If you do not see any lenses, click the "Configuration" tab, click the "Create Default Lenses" button, then return to the ray trace display.
-
-Navigation within the ray trace display is as intuitive as I could make it:
-
-* To zoom in and out, use your mouse wheel.
-
-* To pan around the display, just click the display and drag using the mouse.
-
-* To move a lens from one place to another in the display, simply click the lens and drag it.
-
-Notice that panning the display itself requires you to click outside any lenses, then drag. To move a lens, click directly on the lens you want to move and drag it.
-
-Hint 1: The display has two dimensions, "x" and "y". "x" is the horizontal dimension, and positive values for x move to the right. "y" is the vertical dimension, and positive values for y move up.
-
-Hint 2: To move large distances, first zoom out, then pan, then zoom in again. This saves time compared to clicking and dragging multiple times.
-
-Hint 3: To determine the position of something in the display, for example the location fo a focal point, simply point the mouse cursor at the point of interest and read the mouse x and y positon in the control panel below the display.
-
-The default setup shows six light beams passing from right to left, through the lenses. The mathematical methods used in this program are efficient enough that (with a moderately fast computer) you can move the lenses around and see how this changes the beam paths -- in real time. Try it -- move the lenses around (click on a lens and drag it) and observe the changing beam paths.
-
-Notice that, when you click a lens, its background color changes and the "Lens Control" panel below the display is enabled. This panel allows you to change the characteristics of your lenses -- focal length, size, curvature, and many other things. Feel free to experiment with this panel's settings, see how they change the appearance of the lenses and the beams.
-
-To change the settings for a lens, first click the lens, then use the lens control panel to make the changes you want.
-
-It will help to know a little about optics to understand what you are seeing. If you already know the basics, you can safely skip the next few lines.
-
-** The Basics of Lenses
-
-Very basically, a lens is a simple way to bend light beams. Imagine a row of soldiers marching, side by side. To change the direction they are marching, it is necessary to make some of the soldiers slow down temporarily. Now let this picture of marching soldiers help you imagine a light wave, traveling through space. Just as with the soldiers, to make the wave change direction, you have to think of a way to make part of the wave slow down. That is what a lens does -- it selectively slows [...]
-
-A convex lens is thicker in the middle than at the edges, and, as it turns out, light takes longer to pass through glass than through air. What this means is that the light that passes through the middle, thickest part of the lens, is slowed down compared to the light that passes through the thinner parts near the edges of the lens. This has the effect of shaping the wavefront that emerges from the lens -- the middle of the emerging wavefront is delayed, and the wave's overall shape is c [...]
-
-Such a lens could be used to focus parts of a real-world scene onto a piece of film or an image sensor. The ability of a lens to focus accurately is a central issue in lens design and, as it turns out, the most common kind of lens, with a spherical shape, is actually quite a bad lens. Its only advantage is that it is easy to make -- everything after that is downhill.
-
-** Types of Lenses Supported in OpticalRaytracer
-
-OpticalRayTracer will happily let you play with various kinds of simple, spherical lenses in its virtual playground, but it also includes some mathematical methods that allow you to fashion some rather extraordinarily good lenses called "hyperboloids," famous for their accuracy ... and their difficulty of manufacture. These kinds of lenses are so expensive that it is simpler -- and much less expensive -- to build and test such lenses using OpticalRayTracer than to try to purchase real-wo [...]
-
-** Easy Tutorial
-
-I want you to perform your own experiments, but here's a simple tutorial to get you started. Using the default lenses automatically created when you run OpticalRayTracer the first time, temporarily drag the concave lens (the lens at the right) out of the optical path. If you drag it a small distance, it will jump back into place, realigning itself with the beam line (ordinarily this automatic feature is a good thing). So drag it a good distance up or down, temporarily removing it from th [...]
-
-* "Lens Radius" refers to the distance from the center to the edge of the lens itself. Very simple.
-
-* "Sphere radius" refers to imaginary spheres that our lens is composed of. Imagine two spheres of glass floating in space, overlapping to some extent, and then imagine that our lens represents the parts of the spheres that overlap. To help in visualizing this, draw two circles on a piece of paper using a coin and a pencil, and be sure to allow some overlap between the circles. The area where the circles overlap is our lens. Starting with this paper diagram, one need only use imagination [...]
-
-As it turns out, the mathematics behind lenses relies very much on this idea of overlapping spheres, hyperboloids, and some other useful shapes. So if you can mentally picture two overlapping spheres, you will be able to predict what will result from your typing particular numbers into OpticalRayTracer. For example, to create a lens with one side convex and one side flat, you might choose to enter a very, very large radius for one side. Like this:
-
-1. Select the double-convex lens by clicking it.
-
-2. Deselect the "symmetrical" check box, which will allow you to choose different traits for the left and right-hand sides of your lens.
-
-3. Enter "1000" into the "Left Sphere Radius" window and press Enter.
-
-4. You will see that the right-hand side of the lens has become flat, and (because we now have a lens with less overall curvature), the beams travel farther to the right before converging. The lens is said to have a longer focal length.
-
-At this point, you may wonder why an entry defining the left-hand sphere had its effect on the right hand side of our lens. The answer is that the imaginary spheres are overlapping, and the right-hand part of our lens is defined by an imaginary sphere centered to the left of the lens. I mention this now to avoid confusion later on. Refer again to the paper drawing of circles suggested above, noticing that the circle that defines the right-hand side of the overlapped region (e.g the lens) [...]
-
-Now for something a tiny bit more advanced.
-
-1. Select the convex lens by clicking on it.
-
-2. Re-enter "6" for "Left Sphere Radius," press Enter, then enable "symmetrical" again by clicking its checkbox, restoring our lens to its original state -- a simple, symmetrical double-convex lens.
-
-3. Drag the mouse on the display to pan over to the region between x = 5 and 6 (remember that "x" means the horizontal axis), where the beams should now be converging. Once you have centered this part of the display, use the mouse wheel to zoom in a bit for a closer look.
-
-4. While looking as the point where the beams converge, click the "Left Hyperboloid" checkbox (because you selected "symmetrical" above, this entry will affect both sides of our lens). The lens focus should improve.
-
-A quick reality check -- what constitutes an "improvement" in a lens? Well, ideally, all the beams should converge on a single point, rather than taking slightly different paths as they are doing now.
-
-5. Now we are going to fine-tune our lens by entering a value for hyperboloid curvature. Type ".074" into the "Left Curv. Fact." window, and press Enter. If you have entered all the right values up to now, this should produce a nearly perfect focus -- all the beams should converge on a single point, located at about x = 4.996.
-
-To discover how accurate this focus is, simply center the focal point in the display and zoom in on it. Eventually you will get to a point where you can see some small imperfections in the focus. This will become visible about the time OpticalRayTracer can no longer support the level of magnification in use (which happens at a zoom factor of about 15). In any case, this class of lens design is very advanced and (no surprise) very difficult to manufacture.
-
-*** Intermediate Steps
-
-It cannot hurt for you to know something more about lenses than has already been presented, and I will assume this is true as we move along.
-
-** The Lens Controls Panel
-
-Please play with some of the settings in the "Lens Controls" panel to see what effect they have. Notice that you can reposition a lens exactly by entering its x and y coordinates -- this is a way to get around the fact that it is difficult to position a lens precisely using the mouse.
-
-Notice the window marked "IOR". This means "Index of Refraction," a value representing the ratio of the speed of light through the lens in question to a vacuum (with an IOR of 1.0). If you set this value to 1.0, the lens will no longer deflect the light beams, because in essence it has been defined as empty space.
-
-Different glasses have different indices of refraction, a property that is taken advantage of in advanced lens designs. Here's an example design that exploits this fact, and introduces the topic of dispersion.
-
-"Dispersion" is a property of glass in which light beams of different wavelengths travel at different speeds. For example, a blue beam takes longer to move through a lens than a red beam. This causes the two colors (wavelengths) of light to focus at two different places, a trait regarded as a bad thing, called "chromatic aberration."
-
-** Dispersion Experiment
-
-To set up for this experiment, create a lens with these settings (you may want to simply apply these settings to one of the default created lenses):
-
-Symmetrical: selected.
-Lens Radius: 2.0
-Left Sphere Radius: 4
-IOR 1.52
-Dispersion: 59
-X position: 0.0
-Y position: 0.0
-
-Now switch to the "Configuration" panel and enter 2 for "Number of Tracing Beams". Now return to the ray trace display to see the effect.
-
-If all your settings are correct, and if the lens has really been positioned at x = 0, y = 0, the two beams should converge at x = 2.608.
-
-Now we'll add a dispersion calculation. Go to the "Configuration" panel and enter 8 for "Number of Dispersion beams." When you return to the ray trace display, you should see an array of colored beams near the lens focal point. In this mode, OpticalRayTracer creates colored beams, each of which has an associated wavelength. During the calculation of the ray paths, the lens dispersion property is taken into account and, just as in the real world, the lens cannot focus all these wavelength [...]
-
-Moving right along, create a second lens (using the existing second default lens or by pressing the "New" button in the ray trace display) with these properties:
-
-Symmetrical: deselected.
-Lens Radius: 2.0
-Left Sphere Radius: 1000
-Right Sphere radius: -4
-IOR 1.72
-Dispersion: 29
-X position: 0.381
-Y position: 0.0
-
-To get the required accuracy, you will need to position this lens using the control panel's "x" entry window, rather than sliding the lens about using the mouse. If all the settings on both lenses are correct and all the other required settings have been made correctly, you will see all the colored beams converge at x = 11.510, with no evident color spreading.
-
-This, by the way, is a classic solution to the problem of chromatic aberration, using varieties of glass called "crown" and "flint," with differing properties that are exploited to make the light beams converge.
-
-By changing the spacing between the lenses, you will quickly see that this setting is very critical to the outcome, which is why in the real world, such pairs of lenses are often glued together, or placed in a lens cell with a spacer of some durable material to maintain a particular separation.
-
-** Lens Selection with the Mouse
-
-In playing with this lens setup, you may notice it is difficult to select a particular lens, because the lenses are very close together and their colored border-boxes overlap quite a lot. In a case like this, just click again -- the program will cycle through the lenses that could be selected by the location of your click.
-
-*** Advanced and Procedural Steps
-
-Remember that the total number of beams traced is equal to the number of tracing beams (selected in the "Configuration" panel) multiplied by the number of dispersion beams, e. g. there is a dispersion beam for each chosen wavelength, times each tracing beam. So if the display slows down, this could easily be the reason -- too many beams selected. To prevent calculation of dispersion, simply set "Number of Dispersion Beams" to zero.
-
-OpticalRayTracer uses several mathematical methods to produce its results (and you got a copy of all the source files when you downloaded the program, so you can examine the methods in detail).
-
-** Algorithm Description
-
-The program first calculates the location of any intersections between tracing beams and spheres or hyperboloids (our lenses). The collision detection mathematics is rather involved and won't be described in any detail here.
-
-Having acquired a list of all possible points of collision for a particular beam, OpticalRayTracer sorts the list of results along the x dimension, then determines which intersection is next (to the right) along the beam's path.
-
-At this point OpticalRayTracer has determined a point of collision between a tracing ray and a lens. The ray and the lens collision point each have a characteristic angle, which is used in the next computation.
-
-** Snell's Law
-
-"Snell's Law" is a classic optical relationship that, given arguments for incidence angle between two media and indices of refraction for the two media, determines the deflection angle. Expressed in classic form, Snell's Law is:
-
-n1 sin(a1) = n2 sin(a2)
-
-Where:
-
-n1 = index of refraction of medium 1
-a1 = angle within medium 1
-
-n2 = index of refraction of medium 2
-a2 = angle within medium 2
-
-The astute reader will notice that, in passing from a medium like air with an IOR near 1.0, to a lens with an IOR of 1.5 for example, the angle must decrease. And conversely, a beam emerging from glass to air will show an increase in its angle of deflection. Also, it can be seen that an incident angle of zero will not be deflected -- it will remain zero.
-
-In computing refraction, OpticalRayTracer uses this restatement of the Snell's Law equation:
-
-a2 = arcsin(n1 sin(a1) / n2)
-
-Here is a practical example:
-
-Medium 1: air
-
-n1 = 1.0
-a1 = 20 degrees
-
-Medium 2: crown spectacle glass
-
-n2 = 1.52
-a2 = arcsin(1.0 sin(a1) / 1.52) = 13.00365 degrees
-
-** Dispersion Computation
-
-The computation for dispersion follows along similar lines, but with an empirical equation less grounded in physically simple principles (and original with the author). The dispersion equation changes the index of refraction based on the wavelength of the light beam:
-
-ior2 = ior + ((dp - w) * 500000.0) / (abbe * dp * w^2)
-
-Where:
-
-ior = original index of refraction for the medium.
-
-dp = dispersion pivot wavelength, set to 589.3 nm, the sodium yellow line.
-
-w = wavelength of the tracing beam in nm (nanometers).
-
-abbe = Abbe's Number, a value associated with many glasses and that describes its dispersion property. Lower Abbe's numbers result in higher dispersion.
-
-Abbe's Number is arrived at in this way:
-
-abbe = (nd-1)/(nf-nc)
-
-Where:
-
-nf = a medium's index of refraction at the 486.1 nm hydrogen blue line.
-nd = a medium's index of refraction at the 589.3 nm sodium yellow line.
-nc = a medium's index of refraction at the 656.3 nm hydrogen red line.
-
-The Abbe numbers for various media are arrived at in laboratory experiments. My equation simply reverses the relationship between the number and its effects, and within an accuracy of about 1%.
-
-** Global Configuration Details
-
-Hint 1: To change a color in the "Configuration" panel's selection list, simply click the colored button for the value you want to change. A color selection dialog will appear.
-
-Hint 2: All the values in this list, along with a full description of the lenses you create, is preserved between sessions in a file located at [PROGDIR]/OpticalRayTracer.ini on this system.
-
-Here is an explanation of the controls in the Configuration panel:
-
-* Y Baseline Color. This is the color of the reference line at y = 0 in the ray trace display. If you do not want this line to appear, set its color to the color of the display background (see "Display Background" below).
-
-* Grid Color. This is the auto-scaling grid in the ray trace display. Again, you can turn this off by setting its color to that of the background.
-
-* Display Background Color. This color defaults to white, but in some cases, like trying to make out the colored dispersion beams, another color might be better.
-
-* Default Beam Color. This is the beam color that is used when dispersion is not being computed. Remember that dispersion beams have an internally computed color, appropriate to their wavelength.
-
-* Beam Intersection Color. This is the color of the dots that mark the intersections between light beams and lens surfaces.
-
-* Intersection Dot Radius. This numeric entry specifies the size of the dots described above.
-
-* Lens Default Box Color. This color appears surrounding unselected lenses, to aid the user in selecting a lens, and to distinguish a lens selection area from the background. If you prefer no box to appear, as above just set this color to that of the display background.
-
-* Lens Selected Box Color. This color might be better allowed to remain in view, since it tells you which lens has been selected and to which lens the control panel settings apply.
-
-* Lens Outline Color. This color is used to draw the profile of each lens.
-
-* Y Snap-to-Baseline Threshold. This numeric entry sets the threshold for the behavior that returns a lens to the Y baseline when the mouse is released. This is ordinarily a good thing, a feature, but if you prefer this not to happen, set this number to zero. In truth, the feature is always active regardless of the this setting, but if you enter zero, the feature is only active at a threshold value of zero, which ... well, I think you get the idea.
-
-* Tracing Beam Width. This setting is a bit confusing. A setting of zero causes 1-pixel-width lines to be drawn quickly. A setting of 1 or above causes a a more elaborate line-drawing algorithm to be used, one that isn't necessarily better than the default. The primary use of this settings is to create thick lines for special display purposes.
-
-* Number of Tracing Beams. This is a setting with an large impact on program performance. OpticalRayTarcer's algorithms are swift, but choosing a large number of tracing beams can easily disable one of its best features: real-time response to user inputs. Also, it is easy to become overloaded with information as the number of beams increases. It is difficult to take advantage of the information presented by more than about 8 tracing beams. Lots of beams can be entertaining, though.
-
-* Interactions Per beam. This determines the limit to interactions for a given beam, to avoid the computation of pointless internal reflections within a lens, for example. The default value is quite large, but a sufficiently complex lens system might require that it be increased.
-
-* Y Start Beam position and
-* Y End   Beam Position. These values determine the vertical limits for the array of tracing beams. These two numbers are typically set to fall within the chosen radius of your lenses. The default settings of -1.8 and 1.8 means all the beams fall comfortably within the default lens radius of 2 units (remember that a lens diameter is twice its radius).
-
-* X Source Reference Plane. This is the location in the x dimension from which the tracing beams emanate. In the ray trace display, zoom out to see the location of the beam origin, then change this number to see what happens.
-
-* X Target Reference Plane. This is the plane in the x dimension through which the tracing beams pass, regardless of their angle (desribed below). It is set by default to x = 0, so that a lens positioned here will receive the beams regardless of the other option settings.
-
-* Beam Offset Angle. This setting is used to tilt the array of tracing beams, a way to test the off-axis performance of a lens without having to rotate the lens itself. By controlling the source angle you can observe the resulting path through a series of lenses, thus determining the off-axis performance for the entire system.
-
-* Diverging Beam Source. This checkbox causes the tracing beams to originate in and diverge from a point source located in the X source reference plane, rather than being generated all in parallel.
-
-* Number of Dispersion Beams. This setting produces a set of dispersion beams, with appropriate colors and wavelengths, for each tracing beam. These dispersion beams will be deflected by different amounts depending on specific lens settings, in particular the dispersion value.
-
-Remember that the total number of ray trace computations is equal to the number of tracing beams multiplied by the number of dispersion beams, such that choosing 8 tracing beams and 8 dispersion beams results in 64 traces, fine for a fast computer, but not so great for a slower machine. To prevent the generation of dispersion beams and their associated computation overhead, simply set this value to zero.
-
-*** Conclusion
-
-The OpticalRayTracer Home Page is located at http://www.arachnoid.com/OpticalRayTracer, where additional documentation and other resources will be created as time passes. Be sure to visit to make sure you have the latest version of OpticalRayTracer.
-
-There is a great deal of excellent, detailed information about optics on the Web, both theoretical and practical. Google for "optics," "ray tracing" and related topics.
-
-*** User support
-
-Because OpticalRayTracer is free/GPL (but please visit http://www.arachnoid.com/careware anyway), there is no user support. This help file plus the sort of knowledge available in optical textbooks and online should be sufficient to help the user make it productive.
-
-If you detect a bug in OpticalRayTracer, please report it at http://www.arachnoid.com/messages. Make sure what you are reporting is in fact a bug. :)
-
--- P. Lutus, Port Hadlock, WA
\ No newline at end of file
diff --git a/src/opticalraytracer/helpresources/HelpText.html b/src/opticalraytracer/helpresources/HelpText.html
new file mode 100644
index 0000000..2b5a330
--- /dev/null
+++ b/src/opticalraytracer/helpresources/HelpText.html
@@ -0,0 +1,1099 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <!-- UTF8 MODIFIER -->
+    <title>
+      * OpticalRayTracer
+    </title>
+    <style type="text/css">
+table {
+  background:#fffff0;
+}
+
+table tr td, table tr th {
+  border-width:1px;
+  border-style:solid;
+  border-color:black;
+  white-space:nowrap;
+  }
+
+th {
+  text-align:center;
+  font-weight:bold;
+  background:#c0d0c0;
+}
+.article_subtopic {
+  font-weight:bold;
+}
+    </style>
+  </head>
+  <body bgcolor="#ffffff" text="#000000" link="#0000ff" vlink="#800080" alink="#ff0000">
+    
+    <div align="center">
+      <p><h2><img src="../icons/OpticalRayTracer.png" width="32" height="32" title="" alt=""/> OpticalRayTracer #version# Help Page</h2></p>
+      
+      
+      <p><i>A virtual lens/mirror design workshop.</i></p>
+      <p>Copyright © 2014, <a href="http://arachnoid.com/administration/index.html" title="Click for biography">Paul Lutus</a> -- <a href="http://arachnoid.com/messages/index.php">Message Page</a></p>
+      
+      <p>OpticalRayTracer is released under the <a href="http://www.gnu.org/licenses/gpl.html">GPL</a>.</p>
+      
+      <p>Visit the <a href="http://arachnoid.com/OpticalRayTracer">OpticalRayTracer Home Page</a> for more information and to be sure you have the latest version.</p>
+      
+      
+      <p><i>For formatting reasons, users may want to temporarily make the OpticalRayTracer program frame larger to properly read these instructions.</i></p>
+      
+      <p><i>Users may prefer to search this document using the search feature at the bottom of this frame.</i></p>
+      
+      <p><!-- LINK_MENU_START -->
+        <a href="#Introduction">Introduction</a> | <a href="#First_Steps">First Steps</a> | <a href="#The_Basics_of_Lenses">The Basics of Lenses</a><br/>
+        <a href="#Supported_Optical_Elements">Supported Optical Elements</a> | <a href="#Mirrors">Mirrors</a> | <a href="#Absorbers">Absorbers</a><br/>
+        <a href="#Tutorial">Tutorial</a> | <a href="#Lens_Control_Panel">Lens Control Panel</a> | <a href="#Dispersion_Experiment">Dispersion Experiment</a><br/>
+        <a href="#Using_the_Mouse_and_Keyboard">Using the Mouse and Keyboard</a> | <a href="#Importing_and_Exporting_Data">Importing and Exporting Data</a> | <a href="#System_Considerations">System Considerations</a><br/>
+        <a href="#Algorithm_Description">Algorithm Description</a> | <a href="#Snell_s_Law">Snell's Law</a> | <a href="#Dispersion_Computation">Dispersion Computation</a><br/>
+        <a href="#Design_Options">Design Options</a> | <a href="#Configuration_Options">Configuration Options</a> | <a href="#Common_Problems">Common Problems</a><br/>
+        <a href="#Conclusion">Conclusion</a> | <a href="#User_support">User support</a>
+        <!-- LINK_MENU_END --></p>
+      
+      
+      
+      
+    </div>
+    <p></p>
+    
+    <a name="Introduction"></a><div class="article_subtopic">Introduction</div>
+    
+    
+    
+    
+    <blockquote>
+      <p>OpticalRayTracer is a very portable Java program meant to analyze and model systems of lenses. It accurately models the physics of lenses, including the effect known as dispersion. But perhaps the most remarkable thing about OpticalRayTracer is that it updates and displays complex ray tracing paths in real time, as the user moves virtual lenses around on a virtual optical bench. This allows the user to very quickly learn the behavior of a system of lenses, compare, experiment, a [...]
+      
+      <p>OpticalRayTracer places its configuration file in a directory it creates, so your settings and choices are preserved. This directory is located at #userdir# on your machine, and it contains a configuration file named "OpticalRayTracer.ini" containing quite a lot of detailed information unique to your use of the program.</p>
+      
+      
+      
+      <p>I mention this because:</p>
+      
+      <ul>
+        <li>if you want to analyze or process the results of your work with OpticalRayTracer, the file #userdir#/OpticalRayTracer.ini contains a lot of numeric information in plain-text form, and</li>
+        <li>if you have gotten into difficulty and just want to start over, simply delete this file and run OpticalRayTracer again.</li>
+      </ul>
+      
+      
+      
+      <p>This file contains a very detailed snapshot of your last session with OpticalRayTracer, with lens specifications and positions, suitable for exporting into other environments (the same information can be gotten from the <img src="../icons/edit-copy.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> copy-configuration button on the Design toolbar). To create this file and its picture of your optical setup, simply exit OpticalRayTracer, navigate to #userd [...]
+      
+      
+    </blockquote>
+    
+    <a name="First_Steps"></a><div class="article_subtopic">First Steps</div>
+    
+    <blockquote>
+      
+      
+      
+      <p>Since you are reading this, you have successfully installed OpticalRayTracer, and are ready to try some experiments.</p>
+      
+      <p>When it is first run, the program will automatically create two common lenses for you, a double-convex lens and a double-concave lens. Click the <img src="../icons/applications-graphics.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Design" tab and you will most likely see these two default lenses. If you do not see any lenses, click the <img src="../icons/process-stop.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Er [...]
+      
+      <p>Navigation within the ray trace display is as intuitive as I could make it:</p>
+      
+      <ul>
+        <li>To zoom in and out, use your mouse wheel.</li>
+        
+        <li>To pan around the display, just drag the mouse cursor on the display.</li>
+        
+        <li>To move a lens from one place to another in the display, <i>while holding the Shift or Ctrl key down</i> click the lens with the mouse cursor and drag it.</li>
+        
+        <li>There's more detail on these functions below in "Using the Mouse and Keyboard".</li>
+      </ul>
+      
+      <ul>
+        <li>The display has two dimensions, "x" and "y". "x" is the horizontal dimension, and positive values for x move to the right. "y" is the vertical dimension, and positive values for y move up.</li>
+        
+        <li>To move large distances, first zoom out (mouse wheel), then pan (drag mouse cursor), then zoom in again. This saves time compared to clicking and dragging multiple times.</li>
+        
+        <li>To determine the position of something in the display, for example the location of a focal point, simply point the mouse cursor at the point of interest and read the mouse x and y position on the status bar (bottom of program window).</li>
+      </ul>
+      
+      <p>The default setup shows four light beams passing from left to right, through two example lenses. The mathematical methods used in this program are efficient enough that (with a moderately fast computer) you can move the lenses around and see how this changes the beam paths -- in real time. Try it -- move the lenses around (hold the Shift or Ctrl key down, click a lens and drag it) and observe the changing beam paths.</p>
+      
+      <p>Notice that, when you click a lens, the lens changes color and the design control panel below the display becomes active. This panel allows you to change the characteristics of your lenses -- focal length, size, curvature, and many other things. Feel free to experiment with this panel's settings -- see how they change the appearance of the lenses and light beams.</p>
+      
+      <ul>
+        <li>If you make a change you don't like, simply press the <img src="../icons/edit-undo.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Undo" button to change back.</li>
+        
+        <li>To change the settings for a lens, first click the lens, then use the lens control panel to make the changes you want.</li>
+      </ul>
+      
+      <p>It will help to know a little about optics to understand what you are seeing. If you already know the basics, you can safely skip the next few lines.</p>
+      
+    </blockquote>
+    <a name="The_Basics_of_Lenses"></a><div class="article_subtopic">The Basics of Lenses</div>
+    
+    <blockquote>
+      
+      <p>Very basically, a lens is a simple way to bend light beams. Imagine a row of soldiers marching, side by side. To change the direction they're marching, it is necessary to make some of the soldiers slow down temporarily. Now let this picture of marching soldiers help you imagine a light wave, traveling through space. Just as with the soldiers, to make the wave change direction, you have to think of a way to make part of the wave slow down. That is what a lens does -- it selective [...]
+      
+      <p>A convex lens is thicker in the middle than at the edges, and, as it turns out, light takes longer to pass through glass than through air. What this means is that the light that passes through the middle, thickest part of the lens, is slowed down compared to the light that passes through the thinner parts near the edges of the lens. This has the effect of shaping the wavefront that emerges from the lens -- the middle of the emerging wavefront is delayed, and the wave's overall s [...]
+      
+      <p>Such a lens could be used to focus parts of a real-world scene onto a piece of film or an image sensor. The ability of a lens to focus accurately is a central issue in lens design and, as it turns out, the most common kind of lens, with a spherical shape, is actually not a very good design. Its only advantage is that it's easy to make -- everything after that is downhill.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Supported_Optical_Elements"></a><div class="article_subtopic">Supported Optical Elements</div>
+    
+    <blockquote>
+      
+      <p>OpticalRayTracer supports a variety of optical elements:</p>
+      
+      <ul>
+        <li>Lenses with these curvatures:
+          <ul>
+            <li>Spherical -- a classic lens, dating back to the time of Galileo, and the most common lens type. Its surface can be thought of as the intersection of two spheres. Indeed, in optical modeling software including OpticalRayTracer, that's exactly the mathematical approach taken -- a double-convex lens is modeled as the intersection of two imaginary spheres that give the lens its shape and characteristics.</li>
+            <li>Parabolic -- although most useful in modeling the mirror types supported by OpticalRayTracer, lenses may aso be given a parabolic profile.</li>
+            <li>Hyperbolic -- this lens type has become increasingly popular as its ability to overcome spherical aberration becomes more widely understood. Although difficult to manufacture, hyperbolic lenses are far superior to other lens types for ideal focus and other characteristics.</li>
+            <li>Planar -- an optically flat plane, used primarily for mirrors and beam absorbers.</li>
+          </ul>
+        </li>
+        <li>Mirrors. OpticalRayTracer can turn any optical surface into a mirror, so all the above-described curvatures can be applied to a reflecting surface. This is particularly important for telescope design and in the design of certain kinds of solar energy devices.</li>
+        <li>Absorbers. These are used to terminate optical paths in a controlled way at a specificed distance. Once absorbed, a light ray appears in OpticalRayTracer's data table for analysis.</li>
+      </ul>
+      
+      <p>OpticalRayTracer will let you play with various kinds of simple, spherical lenses in its virtual playground, but it also includes some mathematical methods that allow you to fashion some rather extraordinarily good lenses called "hyperboloids," famous for their accuracy ... and their difficulty of manufacture. These kinds of lenses are so expensive that it is simpler -- and much less expensive -- to build and test such lenses using a program like OpticalRayTracer than to try to  [...]
+      
+    </blockquote>
+    
+    <a name="Mirrors"></a><div class="article_subtopic">Mirrors</div>
+    
+    <blockquote>
+      
+      <p>Recent OpticalRayTracer versions allow the creation of mirrors -- more specifically, lenses that behave like mirrors. The user creates a suitable flat or curved surface and clicks the design tab's "Reflector" checkbox. This feature allows diverting light beams out of the normal optical path, as well as the creation of concave and convex mirrors, such as are used in astronomical telescopes.</p>
+      
+      <p>To produce a generic mirror, click the "New mirror/absorber" toolbar button. Now you may change the mirror's characteristics, just as for a lens. Remember that, if it has a curved surface, a mirror can act as a lens. To tilt the mirror in the optical path, choose an appropriate value for the angle entry. By creating two mirrors, one concave reflector as an objective lens and a smaller diagonal mirror, it's possible to build a virtual Newtonian telescope and analyze the beam paths.</p>
+      
+      <p>If you encouter a case where a mirror won't reflect light, try increasing the mirror's thickness. OpticalRayTracer tries to avoid drawing the same object repeatedly, and it does this by measuring very small distances, and skipping objects that it thinks it may already have drawn. If your mirror is thin enough, OpticalRayTracer may not "see" it. This is true in nature too, but for a different reason.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Absorbers"></a><div class="article_subtopic">Absorbers</div>
+    
+    <blockquote>
+      
+      <p>An absorber is a third kind of supported optical object, and is a variation on mirrors. It's used in cases where the user wants to eliminate one or more rays from the calculation. To create an absorber, just create a mirror as explained above, then select "Absorb" from the design tab's option checklist. All rays that intersect with an absorber are terminated and play no further part in the optical calculation.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Tutorial"></a><div class="article_subtopic">Tutorial</div>
+    
+    <blockquote>
+    
+     <div align="center"><img src="overlapDiagram.png" title="" alt=""/><br/>
+      Figure 1: Lens defined by overlapping spheres</div>
+      
+      <p>I want you to perform your own experiments, but first, here's a simple tutorial to get you started. Using the default lenses automatically created when you run OpticalRayTracer the first time, temporarily drag the concave lens (the lens at the right) out of the optical path (remember: to move a lens, press Shift or Ctrl as you drag your mouse cursor). If you drag the lens a small distance, it will jump back into place, realigning itself with the beam line (ordinarily this automa [...]
+      
+      <ul>
+        <li>"Lens Radius" refers to the vertical distance from the center to the edge of the lens, the overlapping section of Figure 1.</li>
+        
+        <li>"Sphere radius" refers to the radius of one of the imaginary spheres from which our lens is composed. Referring to Figure 1 above, imagine two spheres of glass floating in space, overlapping to some extent, then imagine that our lens represents the overlapping part of the spheres.</li>
+      </ul>
+      
+      <p>As it turns out, the mathematics behind lenses relies very much on this idea of overlapping spheres, hyperboloids, and some other useful shapes (as fully explained <a href="http://arachnoid.com/OpticalRayTracer_technical">here</a>). So if you can mentally picture two overlapping spheres as shown in Figure 1, you should be able to predict the result of entering particular numbers into OpticalRayTracer. For example, to create a lens with one side convex and one side flat, you may  [...]
+      
+      <ul>
+        <li>Select the double-convex lens by clicking it.</li>
+        
+        <li>Deselect the "symmetrical" check box, which allows you to choose different traits for the left and right-hand sides of your lens.</li>
+        
+        <li>Select "Planar" for the left side curvature class.</li>
+        
+        <li>You will see that the right-hand side of the lens has become flat, and (because we now have a lens with less overall curvature), the beams travel farther to the right before converging. The lens is said to have a longer <i>focal length</i>.</li>
+      </ul>
+      
+     
+      
+      <a name="LeftRightReversal"></a><p><b>Left-Right Reversal:</b> At this point, you may wonder why an entry defining the left-hand sphere's radius had its effect on the right hand side of our lens. The answer is that, as explained above, a lens is defined by two overlapping spheres (see Figure 1 above), and the <i>right-hand</i> surface of our lens is defined by a sphere centered to the <i>left</i> of the lens, and vice versa.</p>
+      
+      <p>Now for something a tiny bit more advanced.</p>
+      
+      <ul>
+        <li>Select the default convex lens by clicking on it.</li>
+        
+        <li>Re-select "Spherical" for the left curvature class and enable "symmetrical" again by clicking its checkbox, restoring our lens to its original state -- a simple, symmetrical double-convex lens.</li>
+        
+        <li>Drag the mouse on the display to pan over to the region between x = 4 and 6 (remember that "x" means the horizontal axis), where the beams should now be converging. Once you have centered this part of the display, use the mouse wheel to zoom in a bit for a closer look.</li>
+        
+        <li>While looking at the point where the beams converge, select the "Hyperbolic" curvature class from the provided drop-down list (because you selected "symmetrical" above, this entry will affect both sides of our lens). The lens focus should greatly improve.</li>
+        
+        <li>A quick reality check -- what constitutes an "improvement" in a lens? Well, ideally, all the beams should converge on a single point, rather than taking slightly different paths as they are doing now.</li>
+        
+        <li>Now we are going to fine-tune our lens by entering a custom value for hyperbolic curvature. Type "40" into the left "Hyperbolic Factor" entry window, and press Enter. If you have entered all the right values up to now, this should produce a nearly perfect focus -- all the beams should converge on a single point, located at roughly x = 5.4.</li>
+        <li>To discover how accurate this focus is, center the focal point in the display and zoom in on it (use your mouse wheel). Eventually you will get to a point where you can see some small imperfections in the focus. (This will become visible at a zoom factor of about 5.)</li>
+        <li>At this point you can fine-tune the lens' properties by carefully adjusting its hyperbolic curvature factor, which has an optimum near the value of 39. One way to make this adjustment is to position your mouse cursor over the hyperbolic curvature entry window and spinning the mouse wheel.</li>
+      </ul>
+      
+      <p>Hyperbolic-curvature lenses are an example of advanced optics and were once very difficult to manufacture. As computers come to play a greater role in optical manufacturing, these high-performance lenses should become more common.</p>
+      
+    </blockquote>
+    
+    <a name="Lens_Control_Panel"></a><div class="article_subtopic">Lens Control Panel</div>
+    
+    <blockquote>
+      
+      <p>Play with some of the settings in the lens control panel (the panel located on the "Design" tab) to see what effect they have. Notice that you can reposition a lens exactly by entering its x and y coordinates -- this is a way to get around the fact that it is difficult to position a lens precisely using the mouse.</p>
+      
+      <p>Notice the entry marked "IOR". This means "Index of Refraction," a value representing the ratio of the speed of light through the lens in question to a vacuum (which has an IOR of 1.0). If you set this value to 1.0, the lens will no longer deflect the light beams, because the lens has in essence been redefined as empty space.</p>
+      
+      <p>Different glasses have different indices of refraction, a property we can take advantage of in advanced lens designs. Here's an example design that displays the effect called dispersion using differently colored light beams.</p>
+      
+      <p>"Dispersion" is a property of glass in which light beams of different wavelengths travel at different speeds. For example, a blue beam takes longer to move through a lens than a red beam. This causes the two colors (wavelengths) of light to focus at two different places, a trait regarded as a bad thing, called "chromatic aberration."</p>
+      
+    </blockquote>
+    
+    <a name="Dispersion_Experiment"></a><div class="article_subtopic">Dispersion Experiment</div>
+    
+    <blockquote>
+      
+      <p>This is an optional digression for the curious. To set up for this experiment:</p>
+      
+      <ul>
+        <li>Delete any existing lenses.</li>
+        <li>Switch to the "Configuration" panel and enter "0" for "Snap-To-Grid Value" and "4" for "Light Beam Count".</li>
+        <li>Create a lens with these settings (or you can copy its definition from this page -- see below):
+          
+          <p><ul>
+              <li>Symmetrical: selected.</li>
+              <li>Lens Radius: 2.0</li>
+              <li>Left Sphere Radius: 5.0</li>
+              <li>Right Sphere Radius: 5.0</li>
+              <li>Edge Thickness: 0</li>
+              <li>Center Thickness: 0.8348</li>
+              <li>Index of Refraction: 1.52</li>
+              <li>Dispersion (Abbe number): 59</li>
+              <li>X position: 0.0</li>
+              <li>Y position: 0.0</li>
+          </ul></p>
+          
+        </li>
+        
+        
+        <li><a href="file:/opticalraytracer/helpresources/dispersionTestLens1.txt">Click here ("Crown" lens)</a> to copy this lens definition onto the clipboard, then paste it into the experimental setup using the display context (right-click) menu (use "Paste: defined position").</li>
+        
+        <li>Now return to the ray trace display to see the effect.</li>
+        
+        <li>If all your settings are correct, and if the lens has really been positioned at x = 0, y = 0, the two beams should converge at about x = 3.7.</li>
+        
+      </ul>
+      
+      <p>Now we'll add a dispersion calculation.</p>
+      
+      <ul>
+        
+        <li>Go to the "Configuration" panel and enter "8" for "Dispersion beam count."</li>
+      </ul>
+      <p>When you return to the ray trace display, you should see an array of colored beams near the lens focal point. In this mode, OpticalRayTracer creates colored beams, each of which has an associated wavelength. During the calculation of the ray paths, the lens dispersion property is taken into account and, just as in the real world, the lens cannot focus all these wavelengths onto a single point.</p>
+      
+      <ul>
+        <li>Moving right along, create a second lens (or copy its definition from this page -- see below) with these properties:
+          
+          <p><ul>
+              <li>Symmetrical: deselected.</li>
+              <li>Lens Radius: 2.0</li>
+              <li>Left Sphere Radius: (planar)</li>
+              <li>Right Sphere radius: -5</li>
+              <li>Edge Thickness: 0.6</li>
+              <li>Center Thickness: 0.1828</li>
+              <li>Index of Refraction: 1.72</li>
+              <li>Dispersion (Abbe number): 29</li>
+              <li>X position: 0.3810</li>
+              <li>Y position: 0.0</li>
+          </ul></p>
+          
+          
+        </li>
+        <li><a href="file:/opticalraytracer/helpresources/dispersionTestLens2.txt">Click here ("Flint" lens)</a> to copy this lens definition onto the clipboard, then paste it into the experimental setup using the display context (right-click) menu (use "Paste: defined position").</li>
+        
+        <li>If all the settings on both lenses are correct and all the other required settings have been made correctly, you will see all the colored beams converge at about x = 14.0, with <i>very little</i> color dispersion.</li>
+        
+        <li>It's possible to adjust the spacing between the lenses, which is both very sensitive and critical for the effect being modeled:
+          
+          <p> <ul>
+              <li>Select the concave lens, the lens at the right, by clicking it -- it will become green.</li>
+              <li>Pan over to the focal point (drag your mouse) and zoom in on the point where the beams cross (mouse wheel), choose a high magnification like 10 (readable on the status bar).</li>
+              <li>Select the Design tab and click the X Position entry window.</li>
+              <li>Now, while holding down the Shift and Alt keyboard keys (to greatly reduce the rate of change), spin your mouse wheel over the X Position window, and notice the effect on the focal point's position and quality.</li>
+              <li>This procedure tunes the spacing betweeen the lenses by a very small amount, to optimize the dispersion effect.</li>
+              
+          </ul></p>
+          
+          <li>To skip the above step-by-step procedure and configure the above experiment with just one action, <a href="file:/opticalraytracer/helpresources/dispersionTestSetup.txt">click here</a> to copy the complete program state, and paste the result using the main toolbar's <img src="../icons/edit-paste.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Paste Full Configuration" button (located on the "Design" tab).</li>
+          
+        </li>
+        
+      </ul>
+      
+      <p>This, by the way, is a <a href="http://en.wikipedia.org/wiki/Achromatic_lens">classic solution</a> to the problem of chromatic aberration, using varieties of glass called "crown" and "flint," with differing properties that are exploited to make the light beams converge.</p>
+      
+      <p>By changing the spacing between the two lenses, you will quickly see that this setting is very critical to the outcome, which is why in the real world, such pairs of lenses are often glued together or placed in a lens cell with a spacer of some durable material to maintain the required separation.</p>
+      
+    </blockquote>
+    
+    <a name="Using_the_Mouse_and_Keyboard"></a><div class="article_subtopic">Using the Mouse and Keyboard</div>
+    
+    <blockquote>
+      
+      <p><b>Graphic display</b></p>
+      
+      <p>While playing with lens configurations, you may sometimes notice it is difficult to select a particular lens because the lenses are close together and their selection territories overlap. In a case like this, just click the display repeatedly -- the program will cycle through the lenses that could be selected at the location of your click. You also have the option of cycling among the objects by clicking the <img src="../icons/view-refresh.png" width="22" height="22" style="vert [...]
+      
+      <p>The graphic display pays attention to the mouse's various buttons and wheel, plus certain keyboard keys. Here's a list of mouse-related inputs and actions:</p>
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          
+          <tr>
+            <th>
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          
+          <tr>
+            <td>
+              Click once
+            </td>
+            <td>
+              Select an object near the mouse cursor
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Click more than once
+            </td>
+            <td>
+              Cycle through objects near mouse cursor
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Double-click
+            </td>
+            <td>
+              List properties of nearest line
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Drag mouse
+            </td>
+            <td>
+              Pan display
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Drag mouse with Shift or Ctrl keys
+            </td>
+            <td>
+              Move selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel
+            </td>
+            <td>
+              Zoom display
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel with Shift key
+            </td>
+            <td>
+              Rotate selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel with Ctrl key
+            </td>
+            <td>
+              Resize selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Most of the above actions with Alt key
+            </td>
+            <td>
+              Slower change
+            </td>
+          </tr>
+          
+        </table>
+        
+      </blockquote>
+      
+      <p>Here is a list of keyboard-related inputs and actions:</p>
+      
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          
+          <tr>
+            <th>
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          <tr>
+            <td>
+              Tab
+            </td>
+            <td>
+              Move forward through all program controls
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Shift|Tab
+            </td>
+            <td>
+              Move in reverse through all program controls
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Alt-D
+            </td>
+            <td>
+              Design tab
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Alt-C
+            </td>
+            <td>
+              Configure tab
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Alt-T
+            </td>
+            <td>
+              Table tab
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Alt-H
+            </td>
+            <td>
+              Help tab
+            </td>
+          </tr>
+          <tr>
+            <td>
+              F1
+            </td>
+            <td>
+              Concise help dialog
+            </td>
+          </tr>
+          <tr>
+            <td>
+              M or context menu key
+            </td>
+            <td>
+              Context [M]enu
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Enter (over object)
+            </td>
+            <td>
+              Select object under cursor
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Enter (outside objects)
+            </td>
+            <td>
+              List properties of nearest line
+            </td>
+          </tr>
+          <tr>
+            <td>
+              L
+            </td>
+            <td>
+              [L]ist properties of nearest line (even inside objects)
+            </td>
+          </tr>
+          <tr>
+            <td>
+              O
+            </td>
+            <td>
+              Cycle through [O]bject selections
+            </td>
+          </tr>
+          <tr>
+            <td>
+              U
+            </td>
+            <td>
+              [U]nselect all objects
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Up/down/left/right Arrow keys
+            </td>
+            <td>
+              Pan display
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Ctrl|Arrow or Shift|Arrow keys
+            </td>
+            <td>
+              Move selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              +/- or Home/End
+            </td>
+            <td>
+              Zoom display in/out
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Ctrl|(+/-) or Ctrl|(Home/End)
+            </td>
+            <td>
+              Resize selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Shift|(+/-) orShift|(Home/End)
+            </td>
+            <td>
+              Rotate selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Most of the above actions with Alt key
+            </td>
+            <td>
+              Slower change
+            </td>
+          </tr>
+          
+        </table>
+        
+      </blockquote>
+      
+      
+      
+      
+      <p><b>Text Entry Fields</b></p>
+      
+      
+      <p>Virtually all OpticalRayTracer's text entry fields can be changed by placing the mouse cursor over them and spinning the mouse wheel. If the rate of change is too fast, hold down the shift key while spinning the mouse. If that rate is also too fast, hold down the shift and Alt keys together while spinning the mouse.</p>
+      
+      <p>
+        These actions can be gotten with some special keyboard keys also -- the up and down arrow keys will change the value by +1 and -1 respectively, with smaller changes if the shift and/or Alt keys are held down, just as with the mouse wheel example above. Here is a full list of these special controls:
+      </p>
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          
+          <tr>
+            <th>
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel
+            </td>
+            <td>
+              Value increased/decreased by 1
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              up/down arrow keys
+            </td>
+            <td>
+              Value increased/decreased by 1
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Page Up/Page Down keys
+            </td>
+            
+            <td>
+              Value increased/decreased by 10
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Home/End keys
+            </td>
+            <td>
+              Value increased/decreased by 100
+            </td>
+            
+          </tr>
+          <tr>
+            <td>
+              Esc key
+            </td>
+            <td>
+              Change sign (+-) of associated entry
+            </td>
+            
+          </tr>
+        </table>
+        
+      </blockquote>
+      
+      <p>The text-field mouse-wheel and keyboard actions listed above can be modified by these accompanying keystrokes:
+      </p>
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          <tr>
+            <th>
+              
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          <tr>
+            <td>
+              Wheel/keyboard with Shift key
+            </td>
+            <td>
+              Amount of change divided by 10
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Wheel/keyboard with Alt key
+            </td>
+            <td>
+              Amount of change divided by 100
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Wheel/keyboard with both Shift and Alt keys
+            </td>
+            <td>
+              Amount of change divided by 1000
+            </td>
+          </tr>
+        </table>
+        
+      </blockquote>
+      
+      
+      
+    </blockquote>
+    
+    <a name="Importing_and_Exporting_Data"></a><div class="article_subtopic">Importing and Exporting Data</div>
+    
+    
+    <blockquote>
+      
+      <p>OpticalRayTracer has a number of methods for writing and reading data to/from the world at large, primarily by way of the system clipboard.</p>
+      
+      <ul>
+        <li>To create a copy of the specifications for a lens you've designed, simply click the lens, press the right mouse button and choose <img src="../icons/edit-copy.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy".</li>
+        
+        <li>To paste that lens somewhere else, move the mouse cursor to the desired destination point, press the right mouse button and select <img src="../icons/edit-paste.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Paste: mouse cursor."</li>
+        
+        <li>You can save lens descriptions in other locations, or even paste them into an e-mail for a friend, by pasting the lens description from the system clipboard. You can also make a copy of the entire experimental setup -- lenses, colors, zoom levels, everything -- by clicking the "Copy" button on the main toolbar below the graphic display (not the context-menu copy button). This places a full description of OpticalRayTracer's present state -- all the lenses and mirrors, plus pro [...]
+        
+        <li>To make a graphic copy of the workspace display, click the <img src="../icons/applications-multimedia.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy Workspace" toolbar button, then open a graphic image editor and choose "Paste".</li>
+        
+        <li><p>To create a plain-text table of all the generated lines and place it on the system clipboard, click the <img src="../icons/document-save.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy Table" toolbar button. The generated table can be easily imported into databases and spreadsheets. Note also that you can get information about individual lines by double-clicking the display near the line of interest. You can also use the line properties o [...]
+          
+          
+          <p>Here's an example of an exported data table generated for a simple Newtonian reflector with three components -- a primary mirror, a diagonal reflector, and an eyepiece lens, all named for clarity:</p>
+          
+          
+          <blockquote>
+            <table cellspacing="-1" cellpadding="4" class="bordered">
+              
+              <tr><th>From</th><th>To</th><th>Source</th><th>Destination</th><th>DestinationType</th><th>FromX</th><th>FromY</th><th>ToX</th><th>ToY</th><th>DeltaX</th><th>DeltaY</th><th>Magnitude</th><th>BeamAngle</th><th>SurfaceNormalAngle</th><th>WavelengthNM</th></tr>
+              <tr><td class = "lj">Beam Origin</td><td class = "lj">Reflection</td><td class = "lj">Origin Ray 1</td><td class = "lj">Primary Mirror</td><td class = "lj">Mirror</td><td >-30.0000</td><td >-1.8000</td><td >7.7244</td><td >-1.8000</td><td >37.7244</td><td >2.2204e-16</td><td >37.7244</td><td >3.3724e-16</td><td >173.4166</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Reflection</td><td class = "lj">Reflection</td><td class = "lj">Primary Mirror</td><td class = "lj">Diagonal Mirror</td><td class = "lj">Mirror</td><td >7.7244</td><td >-1.8000</td><td >1.2962</td><td >-0.2962</td><td >-6.4282</td><td >1.5038</td><td >6.6017</td><td >166.8332</td><td >-315.0002</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Reflection</td><td class = "lj">Refraction</td><td class = "lj">Diagonal Mirror</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Lens</td><td >1.2962</td><td >-0.2962</td><td >0.6519</td><td >2.4582</td><td >-0.6443</td><td >2.7544</td><td >2.8288</td><td >103.1664</td><td >-102.5668</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Refraction</td><td class = "lj">Refraction</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Lens</td><td >0.6519</td><td >2.4582</td><td >0.6461</td><td >2.5405</td><td >-0.0058</td><td >0.0823</td><td >0.0825</td><td >94.0306</td><td >-77.2203</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Refraction</td><td class = "lj">Termination</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Virtual space boundary</td><td class = "lj">Domain Limit</td><td >0.6461</td><td >2.5405</td><td >1.6467</td><td >100.0000</td><td >1.0006</td><td >97.4595</td><td >97.4646</td><td >89.4118</td><td >0.0000e+00</td><td >589.3000</td></tr>
+            </table>
+            
+            
+            
+            
+            
+            
+          </blockquote>
+          
+          
+          
+          
+        </li>
+        
+      </ul>
+    </blockquote>
+    
+    <a name="System_Considerations"></a><div class="article_subtopic">System Considerations</div>
+    
+    <blockquote>
+      
+      <p>Remember that OpticalRayTracer is a Java application, which means it needs a current Java runtime engine. If the behavior of your copy of OpticalRayTracer doesn't correspond with the description provided here, it probably means your installed version of Java is not up-to-date. To remedy this, visit <a href="http://java.com">http://java.com</a> to update your Java installation (Java is free).</p>
+      
+      <p>Remember also that the total number of beams traced is equal to the number of tracing beams (selected in the "Configuration" panel) multiplied by the number of dispersion beams, e. g. there is a dispersion beam for each chosen wavelength, times each tracing beam. So if the display slows down, this could easily be the reason -- too many beams selected. To prevent calculation of dispersion, simply set "Dispersion Beam Count" to zero.</p>
+      
+    </blockquote>
+    
+    <a name="Algorithm_Description"></a><div class="article_subtopic">Algorithm Description</div>
+    
+    <blockquote>
+      
+      (For a more complete presentation of this topic, visit the <a href="http://arachnoid.com/OpticalRayTracer_technical/index.html">OpticalRayTracer technical discussion page</a>.)
+      
+      <p>OpticalRayTracer first calculates the location of any intersections between tracing beams and spheres or hyperboloids (our lenses). The collision detection mathematics is rather involved and won't be described in any detail here.</p>
+      
+      <p>Having acquired a list of all possible points of collision for a particular beam, OpticalRayTracer sorts the list of results along the current direction, then determines which intersection is closest along the beam's path.</p>
+      
+      <p>At this point OpticalRayTracer has determined a point of collision between a tracing ray and a lens or mirror. The ray and the lens collision point each have a characteristic angle, which is used in the next computation.</p>
+      
+    </blockquote>
+    
+    <a name="Snell_s_Law"></a><div class="article_subtopic">Snell's Law</div>
+    
+    <blockquote>
+      <p>"Snell's Law" is a classic optical relationship that, given arguments for incidence angle between two media and indices of refraction for the two media, determines the deflection angle. Expressed in classic form, Snell's Law is:</p>
+      
+      <blockquote>
+        n1 sin(a1) = n2 sin(a2)
+      </blockquote>
+      
+      <p>Where:</p>
+      
+      <ul>
+        <li>n1 = index of refraction of medium 1</li>
+        <li>a1 = angle within medium 1</li>
+        
+        <li>n2 = index of refraction of medium 2</li>
+        <li>a2 = angle within medium 2</li>
+      </ul>
+      
+      <p>The astute reader will notice that, in passing from a medium like air with an IOR near 1.0, to a lens with an IOR of 1.5 for example, the light beam's angle with repect to the surface normal must decrease. And conversely, a beam emerging from glass to air will show an increase in its angle of deflection. It can also be seen that an incident relative angle of zero will not be deflected -- it will remain zero.</p>
+      
+      <p>In computing refraction, OpticalRayTracer uses this restatement of the classic Snell's Law equation:</p>
+      
+      <blockquote>
+        a2 = sin<sup>-1</sup>(sin(a1) n1 / n2)
+      </blockquote>
+      
+      <p>Here's a practical example:</p>
+      
+      
+      
+      <ul>
+        <li>Medium 1: air
+          
+          <ul>
+            <li>n1 = 1.0</li>
+            <li>a1 = 20 degrees</li>
+          </ul>
+        </li>
+        <li>Medium 2: crown spectacle glass
+          
+          <ul>
+            <li>n2 = 1.52</li>
+            <li>a2 = sin<sup>-1</sup>(sin(a1) n1 / n2) = 13.00365 degrees</li>
+          </ul>
+        </li>
+      </ul>
+      
+      
+      <p>The above example (in which n1 < n2) causes the beam to deflect toward the normal line (the line perpendicular to the optical surface). In the reverse case (n1 > n2), the ray is deflected away from the normal line. In some cases this may exceed a "critical angle" such that the beam is deflected back into the refracting medium. This is called "total internal reflection", and in such a case OpticalRayTracer imitates nature by reflecting the beam back into the medium using a  [...]
+      
+      <p>Note: OpticalRayTracer provides very reliable Snell's Law results, as accurate as the entered lens measurements. The values listed in the OpticalRaytracer data table can be relied on for optical analysis purposes within floating-point processing accuracy limitations.</p>
+      
+    </blockquote>
+    
+    <a name="Dispersion_Computation"></a><div class="article_subtopic">Dispersion Computation</div>
+    
+    <blockquote>
+      <p>The computation for dispersion follows along similar lines, but this task is less open to analysis from physically simple principles. My empirical dispersion equation changes the index of refraction based on the wavelength of the light beam:</p>
+      
+      
+      
+      <blockquote>
+        ior' = ior + ((dp - w) 5x10<sup>5</sup>) / (abbe dp w<sup>2</sup>)
+      </blockquote>
+      
+      <p>Where:</p>
+      
+      
+      
+      <ul>
+        <li>ior' = effective index of refraction at wavelength w</li>
+        
+        <li>ior = default index of refraction for the medium.</li>
+        
+        <li>dp = dispersion pivot wavelength, set to 589.3 nm, the sodium yellow line.</li>
+        
+        <li>w = wavelength of the tracing beam in nanometers.</li>
+        
+        <li>abbe = Abbe's Number, a value published for many glasses that describes its dispersion property. Lower Abbe's numbers result in higher dispersion.</li>
+      </ul>
+      
+      <p>Abbe's Number is arrived at in this way:</p>
+      
+      <blockquote>
+        abbe = (nd-1)/(nf-nc)
+      </blockquote>
+      
+      <p>Where:</p>
+      
+      <ul>
+        <li>nf = a medium's index of refraction at the 486.1 nm hydrogen blue line.</li>
+        <li>nd = a medium's index of refraction at the 589.3 nm sodium yellow line.</li>
+        <li>nc = a medium's index of refraction at the 656.3 nm hydrogen red line.</li>
+      </ul>
+      
+      <p>Abbe numbers for various media are arrived at empirically in laboratory experiments. My equation reverses the relationship between the number and its effects, giving a dispersion-modified IOR with an accuracy of about 5% for many common glasses.</p>
+      
+      <p>Note: OpticalRayTracer provides <i>approximate</i> dispersion results, suitable for graphic display purposes but not precise (as explained <a href="http://arachnoid.com/OpticalRayTracer_technical/index.html#h14">here</a>). If very accurate results are required, a formal calculation is recommended.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Design_Options"></a><div class="article_subtopic">Design Options</div>
+    
+    <blockquote>
+      
+      <p>Here is a list of the <img src="../icons/applications-graphics.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> <b>Design</b> tab options and their meanings.</p>
+      
+      <ul>
+        <li>The <b>Name</b> field acceps a custom name for each optical component. A default name is provided, but for complex optical setups, this feature is a nice way to keep track of components. Also, when a table of tracing data is exported, the names are included to help clarify what might otherwise be a confusing list of numerical data.</li>
+        
+        <li>The <b>Symmetrical</b> checkbox specifies that the lens is symmetrical -- the left and right sides are the same. This simplifies configuration for a symmetrical lens becaue only one entry needs to be made for each property.</li>
+        
+        <li>The <b>Radius</b> entry specifies the distance between the center of a lens and its edge. This value is sometimes confused with the left and right Sphere Radius entries discussed below. The difference is that the sphere radii specify two imaginary spheres that construct the lens, while this entry specifies the lens size from center to edge.</li>
+        
+        <li>The <b>Thickness</b> entry specifies the left-to-right thickness of the main body of the lens, apart from its curvature. This entry allows lenses to have a thickness beyond their optical curvature, and is typical of real-world lenses.</li>
+        
+        <li>The <b>X and Y Position</b> entries specify the lens placement in the X (horizontal) and Y (vertical) dimensions. The user can also drag the mouse cursor on the lens image to change these entries.</li>
+        
+        
+        <li>There are two entries, left and right, for these values:
+          
+          <p><ul>
+              <li>The <b>Sphere Radius</b> entry specifies the radius of one of the two spheres from which this lens is constructed. See the explanation at the <a href="http://arachnoid.com/OpticalRayTracer_technical/index.html">OpticalRayTracer Technical Description</a> page.</li>
+              
+              <li>The <b>Curvature</b> text entry field accepts an entry for hyperbolic curve:
+                <ul>
+                  <li>Briefly, the curvature factor represents the location of a plane that bisects a unit cone (a hyperbola can be thought of as a <a href="http://en.wikipedia.org/wiki/Conic_section">conic section</a>).</li>
+                  <li>A factor entry of zero positions the bisecting plane at the cone's apex, which produces a lens with a triangular profile, essentially a prism.</li>
+                  <li>Larger factor entries move away from the cone's apex and produce less extreme hyperbolic curvatures, and very large numbers (i.e. 1 x 10<sup>7</sup>) change the curvature from a hyperbola to a figure approximating a parabola.</li>
+                  <li>For tasks that require hyperbolic curvature, entries from 0 to about 100 represent a range of useful shapes.</li>
+                  <li>For an approximate parabolic curve, for example to design a telescope mirror, enter a number around  1 x 10<sup>7</sup>, the actual numeric value isn't as important as its size, since mathematically speaking, there's only one parabolic curve. Then adjust the size and focal length of the optical element with the usual controls.</li>
+                </ul>
+                
+                
+              </li>
+              
+              <li>The next entry option consists of three "radio buttons" that selects one of the available optical surface curvatures for each side of a lens or mirror -- <b>Spherical</b>, <b>Parabolic</b>, and <b>Hyperbolic</b>, details of which are discussed above.</li>
+              
+          </ul></p>
+          
+          
+          <li>The <b>IOR</b> checkbox specifies the lens material's <a href="http://en.wikipedia.org/wiki/Refractive_index">index of refraction</a>, which is the ratio of the speed of light in the lens material compared to that in a vacuum.</li>
+          
+          <li>The <b>Dispersion</b> entry specifies the amount of <a href="http://en.wikipedia.org/wiki/Dispersion_(optics)">dispersion</a> (a wavelength-dependent property) the lens material possesses.</li>
+          
+          <li>The <b>Angle</b> entry rotates the lens or mirror in the X/Y plane. This is useful for evaluating the off-axis behavior of a lens, and for setting up complex optical configurations, for example by directing beams using angled mirrors. </li>
+          <li>The <b>Active</b> checkbox includes or excludes its associated component in the optical calculation. This is a convenient way to manage complex optical models and interactions -- using this feature one can place multiple components in the same location or path and easily switch between them.</li>
+          
+          <li>As explained above, OpticalRayTracer supports three kinds of optical objects: Refractors, Reflectors and Absorbers:
+            <ul>
+              <li>The <b>Refract</b> option is the default choice for lenses. Rays that intersect with this object will be refracted using the outcome of a Snell's Law calculation.</li>
+              <li>The <b>Reflect</b> option turns a lens into a mirror. Light beams reflect from the lens' surfaces instead of passing through. This option makes it possible to create optical designs with mirrors, including curved mirrors such as one finds in modern telescopes.</li>
+              <li>The <b>Absorb</b> option causes intersecting rays to be absorbed and terminated. This is a convenient way to create a barrier against certain rays that would interfere with a desired result. As with lenses and mirrors, an absorber can be given any desired size or shape.</li>
+            </ul>
+          </li>
+          
+          
+        </li>
+        
+        
+      </ul>
+      
+      
+      
+      
+    </blockquote>
+    
+    <a name="Configuration_Options"></a><div class="article_subtopic">Configuration Options</div>
+    
+    <blockquote>
+      
+      <p>Here is an explanation of the controls in the <img src="../icons/applications-accessories.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> <b>Configure</b> tab:</p>
+      
+      <ul>
+        <li>First, the <b>Color</b> button bar:
+          <p><ul>
+              <li>To change a color in the  <b>Configure</b> tab's selection list, simply click the colored button for the value you want to change (each button has a flyout explanation that appears when you hover the mouse cursor over the button). A color selection dialog will appear.</li>
+              
+              <li>OpticalRayTracer supports color transparency, which means if you adjust the "Alpha" setting in the color selection dialog, OpticalRaytracer will honor your choice and make the associated color transparent in proportion (in this context, "Alpha" means transparency). This is useful in a number of situations, for example when light beams overlap and obscure each other -- when the light beams are transparent, it's easier to find a true focal point.</li>
+              
+              <li><b>Light Source Bar Color</b>. This is the color of the vertical line that marks the light beam origin.</li>
+              
+              <li><b>X/Y Zero Baseline Color</b>. This is the color of the reference lines that appear at x = 0 and y = 0 in the ray trace display. If you do not want these lines to appear, set their color to the color of the display background (see "Display Background" below).</li>
+              
+              <li><b>Grid Color</b>. This is the auto-scaling grid in the ray trace display. Again, you can turn this off by setting its color to that of the background.</li>
+              
+              <li><b>Lens</b> color. This color is used to draw the profile and interior of each lens when not selected.</li>
+              
+              <li><b>Tracing Beams Color</b>. This is the beam color used when dispersion is not being computed. Remember that dispersion beams have an internally computed color, appropriate to their wavelength.</li>
+              
+              <li><b>Beam Intersection Color</b>. This is the color of the arrows that mark the intersections between light beams and lens surfaces, where beams change direction by way of refraction.</li>
+              
+              <li><b>Selected Lens Color</b>. A lens changes to this color when it has been selected for editing, either by clicking the lens directly or by way of the cycle lens toolbar button.</li>
+              
+              <li><b>Display High Background Color</b>. This color defaults to white, but in some cases, like trying to make out the color of dispersion beams, another color might be better.</li>
+              
+              <li><b>Display Low Background Color</b>. This color defaults to black, but the user has the option of choosing a different color.</li>
+          </ul></p>
+          
+        </li>
+        
+        <li><b>Intersection Arrow Size</b>. This numeric entry specifies the size of the arrows that mark the destination points of light beams. To remove the arrows from the display, set this value to zero.</li>
+        
+        <li><b>Snap to grid value</b>. This numeric entry sets the threshold for the behavior that returns a lens to the X and Y grid when the mouse is released. This is ordinarily a good thing, a feature, but if you prefer this not to happen, set this number to zero to disable the feature, or choose a different value for special purposes.</li>
+        
+        <li><b>Beam width</b>. This setting adjusts the width of all image lines. An entry of zero is ignored, the minimum beam width is 1.</li>
+        
+        <li><b>Light beam count</b>. This is a setting with a large impact on program performance. OpticalRayTracer's algorithms are swift, but choosing a large number of tracing beams might disable one of its best features: real-time response to user inputs. Also, it is easy to become overloaded with information as the number of beams increases. It is difficult to take advantage of the information presented by more than about 8 tracing beams. Lots of beams can be entertaining, though.</li>
+        
+        <li><b>Source Y start and end</b>. These values determine the vertical limits for the array of tracing beams. These two numbers are typically set to fall within the chosen radius of your lenses. The default settings of -1.8 and 1.8 means all the beams fall comfortably within the default lens radius of 2 units (remember that a lens diameter is twice its radius).</li>
+        
+        <li><b>Maximum interactions</b>. This determines the limit to interactions for a given beam, to avoid the computation of pointless internal reflections within a lens, for example. The default value is quite large, but a sufficiently complex lens system might require that it be increased.</li>
+        
+        <li><b>X Source plane</b>. This is the location in the x dimension from which the tracing beams emanate. In the ray trace display, zoom out to see the location of the beam origin, then change this number to see what happens.</li>
+        
+        <li><b>X beam rotation plane</b>. This is the plane in the x dimension that represents the pivot point for beam rotation. If an entry is made for beam offset angle below, this will be the plane around which the beams will rotate. The default is zero, and for a specific off-axis lens test, the user has the option of moving the lens to this location, changing this defined plane to be that of the lens, or rotating the lens instead of the beams.</li>
+        
+        <li><b>Beam offset angle</b>. This setting is used to tilt the array of tracing beams, a way to test the off-axis performance of a lens without having to rotate the lens itself. By controlling the source angle you can observe the resulting path through a series of lenses, thus determining the off-axis performance for the entire system.</li>
+        
+        <li><b>Dispersion beam count</b>. This setting produces a set of dispersion beams, with appropriate colors and wavelengths, for each tracing beam. These dispersion beams will be deflected by different amounts depending on specific lens settings, in particular the dispersion value. This is the key to the dispersion effect simulation.</li>
+        
+        <li><b>Interlens epsilon</b>. This setting creates a threshold to distinguish between optical surfaces. If during calculation two surfaces are found to be separated by a small distance, OpticalRayTracer may assume the source and destination are the same surface -- and that may be true. To prevent this from happening, increase this setting. Most OpticalRayTracer numerical entries are large, but this one is normally very small, such that scientific notation is normally used, for ex [...]
+        
+        <li><b>Surface epsilon</b>. This value creates an acceptance boundary around optical surfaces so the ray tracer can identify and interact with them. If a light ray skips over a legitimate object instead of interacting with it, increase this value. If light rays seem to be interacting with the empty space near lenses, decrease this value. For lenses with normal dimensions (i.e. ≤ 10 units), this setting's default value shouldn't need to be changed.</li>
+        
+        <li><b>Virtual space box size</b>. This is an outer limit size for the OpticalRayTracer virtual space. This value prevents anomalous behavior that can result from overly large computation values in the absence of limits.</li>
+        
+        <li><b>Diverging Beams</b>. This checkbox causes the tracing beams to originate in and diverge from a point source located at the X source reference plane, rather than being generated in parallel at that plane.</li>
+      </ul>
+      
+      <p>Remember that all the above values are reset by the <img src="../icons/process-stop.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> Erase & Reset button. If you make settings that cause problems, simply reset the program to its default values.</p>
+      
+      
+      <p>Remember also that the total number of ray trace computations is equal to the number of tracing beams multiplied by the number of dispersion beams, such that choosing 8 tracing beams and 8 dispersion beams results in 64 traces, fine for a fast computer, but not so great for a slower machine. To prevent the generation of dispersion beams and their associated computation overhead, set this value to zero.</p>
+      
+      
+      <p>All the configuration values, along with a full description of the lenses you create, are preserved between sessions in a file located at #userdir#/OpticalRayTracer.ini on this system, and the same information can be copied onto the system clipboard with the toolbar "Copy Configuration" button.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Common_Problems"></a><div class="article_subtopic">Common Problems</div>
+    
+    
+    
+    <ul>
+      <li>When designing systems of lenses, be sure that your lenses do not overlap, that one lens is not located within another, and that the light source is not located within a lens. In short, <i>the light source must be separate from the lenses, and the lenses must be separate from each other</i>.</li>
+      <li>Users may notice that the left-hand lens radius entry affects the right-hand side of the lens, and vice versa. This is as intended, and is <a href="#LeftRightReversal">explained here</a>.</li>
+      <li>If light beams pass through lens surfaces without any interaction, this may result from a <b>Surface Epsilon</b> value (on the <img src="../icons/applications-accessories.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> Configure tab) that is too small for the scale of the lenses -- try increasing the value.</li>
+      <li>The "Center Thickness" value displayed on the design tab is a computed result, it's not a user entry -- it's defined by an optical element's curvature class, edge thickness, element radius and sphere radius.</li>
+       <li>There is a great deal of excellent, detailed information about optics on the Web, both theoretical and practical. Google for "optics," "ray tracing" and related topics.</li>
+    </ul>
+    
+    
+    
+    <a name="Conclusion"></a><div class="article_subtopic">Conclusion</div>
+    
+    <blockquote>
+      <p>The OpticalRayTracer Home Page is located at <a href="http://www.arachnoid.com/OpticalRayTracer">http://www.arachnoid.com/OpticalRayTracer</a>, where additional documentation and other resources are located. Be sure to visit to make sure you have the latest version of OpticalRayTracer.</p>
+      
+    </blockquote>
+    
+    <a name="User_support"></a><div class="article_subtopic">User support</div>
+    
+    <blockquote>
+      
+      <p><a href="http://arachnoid.com/OpticalRayTracer_technical/index.html">Click here</a> for a more detailed technical description of OpticalRayTracer and optical mathematics in general.</p>
+      
+      <p>Because OpticalRayTracer is released under the <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> (but please visit <a href="http://www.arachnoid.com/careware">Careware</a> anyway), there is no user support. This help file plus the sort of knowledge available in optical textbooks and online should be sufficient to help the user make it productive.</p>
+      
+      <p>If you detect a bug in OpticalRayTracer, please report it at <a href="http://www.arachnoid.com/messages">arachnoid.com Messages</a>.</p>
+      
+      
+    </blockquote>
+  </body>
+</html>
diff --git a/src/opticalraytracer/helpresources/HelpText.html~ b/src/opticalraytracer/helpresources/HelpText.html~
new file mode 100644
index 0000000..c525302
--- /dev/null
+++ b/src/opticalraytracer/helpresources/HelpText.html~
@@ -0,0 +1,1092 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <!-- UTF8 MODIFIER -->
+    <title>
+      * OpticalRayTracer
+    </title>
+    <style type="text/css">
+table {
+  background:#fffff0;
+}
+
+table tr td, table tr th {
+  border-width:1px;
+  border-style:solid;
+  border-color:black;
+  white-space:nowrap;
+  }
+
+th {
+  text-align:center;
+  font-weight:bold;
+  background:#c0d0c0;
+}
+.article_subtopic {
+  font-weight:bold;
+}
+    </style>
+  </head>
+  <body bgcolor="#ffffff" text="#000000" link="#0000ff" vlink="#800080" alink="#ff0000">
+    
+    <div align="center">
+      <p><h2><img src="../icons/OpticalRayTracer.png" width="32" height="32" title="" alt=""/> OpticalRayTracer #version# Help Page</h2></p>
+      
+      
+      <p><i>A virtual lens/mirror design workshop.</i></p>
+      <p>Copyright © 2014, <a href="http://arachnoid.com/administration/index.html" title="Click for biography">Paul Lutus</a> -- <a href="http://arachnoid.com/messages/index.php">Message Page</a></p>
+      
+      <p>OpticalRayTracer is released under the <a href="http://www.gnu.org/licenses/gpl.html">GPL</a>.</p>
+      
+      <p>Visit the <a href="http://arachnoid.com/OpticalRayTracer">OpticalRayTracer Home Page</a> for more information and to be sure you have the latest version.</p>
+      
+      
+      <p><i>For formatting reasons, users may want to temporarily make the OpticalRayTracer program frame larger to properly read these instructions.</i></p>
+      
+      <p><i>Users may prefer to search this document using the search feature at the bottom of this frame.</i></p>
+      
+      <p><!-- LINK_MENU_START -->
+        <a href="#Introduction">Introduction</a> | <a href="#First_Steps">First Steps</a> | <a href="#The_Basics_of_Lenses">The Basics of Lenses</a><br/>
+        <a href="#Supported_Optical_Elements">Supported Optical Elements</a> | <a href="#Mirrors">Mirrors</a> | <a href="#Absorbers">Absorbers</a><br/>
+        <a href="#Tutorial">Tutorial</a> | <a href="#Lens_Control_Panel">Lens Control Panel</a> | <a href="#Dispersion_Experiment">Dispersion Experiment</a><br/>
+        <a href="#Using_the_Mouse_and_Keyboard">Using the Mouse and Keyboard</a> | <a href="#Importing_and_Exporting_Data">Importing and Exporting Data</a> | <a href="#System_Considerations">System Considerations</a><br/>
+        <a href="#Algorithm_Description">Algorithm Description</a> | <a href="#Snell_s_Law">Snell's Law</a> | <a href="#Dispersion_Computation">Dispersion Computation</a><br/>
+        <a href="#Design_Options">Design Options</a> | <a href="#Configuration_Options">Configuration Options</a> | <a href="#Conclusion">Conclusion</a><br/>
+        <a href="#User_support">User support</a>
+      <!-- LINK_MENU_END --></p>
+      
+      
+      
+      
+    </div>
+    <p></p>
+    
+    <a name="Introduction"></a><div class="article_subtopic">Introduction</div>
+    
+    
+    
+    
+    <blockquote>
+      <p>OpticalRayTracer is a very portable Java program meant to analyze and model systems of lenses. It accurately models the physics of lenses, including the effect known as dispersion. But perhaps the most remarkable thing about OpticalRayTracer is that it updates and displays complex ray tracing paths in real time, as the user moves virtual lenses around on a virtual optical bench. This allows the user to very quickly learn the behavior of a system of lenses, compare, experiment, a [...]
+      
+      <p>OpticalRayTracer places its configuration file in a directory it creates, so your settings and choices are preserved. This directory is located at #userdir# on your machine, and it contains a configuration file named "OpticalRayTracer.ini" containing quite a lot of detailed information unique to your use of the program.</p>
+      
+      
+      
+      <p>I mention this because:</p>
+      
+      <ul>
+        <li>if you want to analyze or process the results of your work with OpticalRayTracer, the file #userdir#/OpticalRayTracer.ini contains a lot of numeric information in plain-text form, and</li>
+        <li>if you have gotten into difficulty and just want to start over, simply delete this file and run OpticalRayTracer again.</li>
+      </ul>
+      
+      
+      
+      <p>This file contains a very detailed snapshot of your last session with OpticalRayTracer, with lens specifications and positions, suitable for exporting into other environments (the same information can be gotten from the <img src="../icons/edit-copy.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> copy-configuration button on the Design toolbar). To create this file and its picture of your optical setup, simply exit OpticalRayTracer, navigate to #userd [...]
+      
+      
+    </blockquote>
+    
+    <a name="First_Steps"></a><div class="article_subtopic">First Steps</div>
+    
+    <blockquote>
+      
+      
+      
+      <p>Since you are reading this, you have successfully installed OpticalRayTracer, and are ready to try some experiments.</p>
+      
+      <p>When it is first run, the program will automatically create two common lenses for you, a double-convex lens and a double-concave lens. Click the <img src="../icons/applications-graphics.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Design" tab and you will most likely see these two default lenses. If you do not see any lenses, click the <img src="../icons/process-stop.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Er [...]
+      
+      <p>Navigation within the ray trace display is as intuitive as I could make it:</p>
+      
+      <ul>
+        <li>To zoom in and out, use your mouse wheel.</li>
+        
+        <li>To pan around the display, just drag the mouse cursor on the display.</li>
+        
+        <li>To move a lens from one place to another in the display, click the lens while holding the Shift or Ctrl key down, and drag it.</li>
+        
+        <li>There's more detail on these functions below in "Using the Mouse and Keyboard".</li>
+      </ul>
+      
+      <ul>
+        <li>The display has two dimensions, "x" and "y". "x" is the horizontal dimension, and positive values for x move to the right. "y" is the vertical dimension, and positive values for y move up.</li>
+        
+        <li>To move large distances, first zoom out (mouse wheel), then pan (drag mouse cursor), then zoom in again. This saves time compared to clicking and dragging multiple times.</li>
+        
+        <li>To determine the position of something in the display, for example the location of a focal point, simply point the mouse cursor at the point of interest and read the mouse x and y position on the status bar (bottom of program window).</li>
+      </ul>
+      
+      <p>The default setup shows ten light beams passing from left to right, through two example lenses. The mathematical methods used in this program are efficient enough that (with a moderately fast computer) you can move the lenses around and see how this changes the beam paths -- in real time. Try it -- move the lenses around (hold the Shift or Ctrl key down, click a lens and drag it) and observe the changing beam paths.</p>
+      
+      <p>Notice that, when you click a lens, the lens changes color and the design control panel below the display becomes active. This panel allows you to change the characteristics of your lenses -- focal length, size, curvature, and many other things. Feel free to experiment with this panel's settings -- see how they change the appearance of the lenses and light beams.</p>
+      
+      <ul>
+        <li>If you make a change you don't like, simply press the <img src="../icons/edit-undo.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Undo" button to change back.</li>
+        
+        <li>To change the settings for a lens, first click the lens, then use the lens control panel to make the changes you want.</li>
+      </ul>
+      
+      <p>It will help to know a little about optics to understand what you are seeing. If you already know the basics, you can safely skip the next few lines.</p>
+      
+    </blockquote>
+    <a name="The_Basics_of_Lenses"></a><div class="article_subtopic">The Basics of Lenses</div>
+    
+    <blockquote>
+      
+      <p>Very basically, a lens is a simple way to bend light beams. Imagine a row of soldiers marching, side by side. To change the direction they're marching, it is necessary to make some of the soldiers slow down temporarily. Now let this picture of marching soldiers help you imagine a light wave, traveling through space. Just as with the soldiers, to make the wave change direction, you have to think of a way to make part of the wave slow down. That is what a lens does -- it selective [...]
+      
+      <p>A convex lens is thicker in the middle than at the edges, and, as it turns out, light takes longer to pass through glass than through air. What this means is that the light that passes through the middle, thickest part of the lens, is slowed down compared to the light that passes through the thinner parts near the edges of the lens. This has the effect of shaping the wavefront that emerges from the lens -- the middle of the emerging wavefront is delayed, and the wave's overall s [...]
+      
+      <p>Such a lens could be used to focus parts of a real-world scene onto a piece of film or an image sensor. The ability of a lens to focus accurately is a central issue in lens design and, as it turns out, the most common kind of lens, with a spherical shape, is actually not a very good design. Its only advantage is that it's easy to make -- everything after that is downhill.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Supported_Optical_Elements"></a><div class="article_subtopic">Supported Optical Elements</div>
+    
+    <blockquote>
+      
+      <p>OpticalRayTracer supports a variety of optical elements:</p>
+      
+      <ul>
+        <li>Lenses with these curvatures:
+          <ul>
+            <li>Spherical -- a classic lens, dating back to the time of Galileo, and the most common lens type. Its surface can be thought of as the intersection of two spheres. Indeed, in optical modeling software including OpticalRayTracer, that's exactly the mathematical approach taken -- a double-convex lens is modeled as the intersection of two imaginary spheres that give the lens its shape and characteristics.</li>
+            <li>Parabolic -- although most useful in modeling the mirror types supported by OpticalRayTracer, lenses may aso be given a parabolic profile.</li>
+            <li>Hyperbolic -- this lens type has become increasingly popular as its ability to overcome spherical aberration becomes more widely understood. Although difficult to manufacture, hyperbolic lenses are far superior to other lens types for ideal focus and other characteristics.</li>
+            <li>Planar -- an optically flat plane, used primarily for mirrors and beam absorbers.</li>
+          </ul>
+        </li>
+        <li>Mirrors. OpticalRayTracer can turn any optical surface into a mirror, so all the above-described curvatures can be applied to a reflecting surface. This is particularly important for telescope design and in the design of certain kinds of solar energy devices.</li>
+        <li>Absorbers. These are used to terminate optical paths in a controlled way at a specificed distance. Once absorbed, a light ray appears in OpticalRayTracer's data table for analysis.</li>
+      </ul>
+      
+      <p>OpticalRayTracer will let you play with various kinds of simple, spherical lenses in its virtual playground, but it also includes some mathematical methods that allow you to fashion some rather extraordinarily good lenses called "hyperboloids," famous for their accuracy ... and their difficulty of manufacture. These kinds of lenses are so expensive that it is simpler -- and much less expensive -- to build and test such lenses using a program like OpticalRayTracer than to try to  [...]
+      
+    </blockquote>
+    
+    <a name="Mirrors"></a><div class="article_subtopic">Mirrors</div>
+    
+    <blockquote>
+      
+      <p>Recent OpticalRayTracer versions allow the creation of mirrors -- more specifically, lenses that behave like mirrors. The user creates a suitable flat or curved surface and clicks the design tab's "Reflector" checkbox. This feature allows diverting light beams out of the normal optical path, as well as the creation of concave and convex mirrors, such as are used in astronomical telescopes.</p>
+      
+      <p>To produce a generic mirror, click the "New mirror/absorber" toolbar button. Now you may change the mirror's characteristics, just as for a lens. Remember that, if it has a curved surface, a mirror can act as a lens. To tilt the mirror in the optical path, choose an appropriate value for the angle entry. By creating two mirrors, one concave reflector as an objective lens and a smaller diagonal mirror, it's possible to build a virtual Newtonian telescope and analyze the beam paths.</p>
+      
+      <p>If you encouter a case where a mirror won't reflect light, try increasing the mirror's thickness. OpticalRayTracer tries to avoid drawing the same object repeatedly, and it does this by measuring very small distances, and skipping objects that it thinks it may already have drawn. If your mirror is thin enough, OpticalRayTracer may not "see" it. This is true in nature too, but for a different reason.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Absorbers"></a><div class="article_subtopic">Absorbers</div>
+    
+    <blockquote>
+      
+      <p>An absorber is a third kind of supported optical object, and is a variation on mirrors. It's used in cases where the user wants to eliminate one or more rays from the calculation. To create an absorber, just create a mirror as explained above, then select "Absorb" from the design tab's option checklist. All rays that intersect with an absorber are terminated and play no further part in the optical calculation.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Tutorial"></a><div class="article_subtopic">Tutorial</div>
+    
+    <blockquote>
+      
+      <p>I want you to perform your own experiments, but here's a simple tutorial to get you started. Using the default lenses automatically created when you run OpticalRayTracer the first time, temporarily drag the concave lens (the lens at the right) out of the optical path. If you drag it a small distance, it will jump back into place, realigning itself with the beam line (ordinarily this automatic feature is a good thing). So drag it a good distance up or down, temporarily removing i [...]
+      
+      <ul>
+        <li>"Lens Radius" refers to the distance from the center to the edge of the lens itself. Very simple.</li>
+        
+        <li>"Sphere radius" refers to imaginary spheres that our lens is composed of. Imagine two spheres of glass floating in space, overlapping to some extent, and then imagine that our lens represents the parts of the spheres that overlap. To help in visualizing this, draw two circles on a piece of paper using a coin and a pencil, and be sure to allow some overlap between the circles. The area where the circles overlap is our lens. Starting with this paper diagram, one need only use i [...]
+      </ul>
+      
+      <p>As it turns out, the mathematics behind lenses relies very much on this idea of overlapping spheres, hyperboloids, and some other useful shapes. So if you can mentally picture two overlapping spheres, you will be able to predict what will result from your typing particular numbers into OpticalRayTracer. For example, to create a lens with one side convex and one side flat, you might choose to enter a very, very large radius for one side. Like this:</p>
+      
+      <ul>
+        <li>Select the double-convex lens by clicking it.</li>
+        
+        <li>Deselect the "symmetrical" check box, which will allow you to choose different traits for the left and right-hand sides of your lens.</li>
+        
+        <li>Select "Planar" for the left side curvature type.</li>
+        
+        <li>You will see that the right-hand side of the lens has become flat, and (because we now have a lens with less overall curvature), the beams travel farther to the right before converging. The lens is said to have a longer <i>focal length</i>.</li>
+      </ul>
+      
+      <p>At this point, you may wonder why an entry defining the left-hand sphere had its effect on the right hand side of our lens. The answer is that the imaginary spheres are overlapping, and the <i>right-hand</i> part of our lens is defined by an imaginary sphere centered to the <i>left</i> of the lens. I mention this now to avoid confusion later on. The circle that defines the right-hand side of the overlapped region (e.g. the lens) is centered to the left of the overlap area.</p>
+      
+      <p>Now for something a tiny bit more advanced.</p>
+      
+      <ul>
+        <li>Select the default convex lens by clicking on it.</li>
+        
+        <li>Re-select "Spherical" for the left curvature type and enable "symmetrical" again by clicking its checkbox, restoring our lens to its original state -- a simple, symmetrical double-convex lens.</li>
+        
+        <li>Drag the mouse on the display to pan over to the region between x = 4 and 6 (remember that "x" means the horizontal axis), where the beams should now be converging. Once you have centered this part of the display, use the mouse wheel to zoom in a bit for a closer look.</li>
+        
+        <li>While looking as the point where the beams converge, click the left "Hyp" checkbox (because you selected "symmetrical" above, this entry will affect both sides of our lens). The lens focus should greatly improve.</li>
+        
+        <li>A quick reality check -- what constitutes an "improvement" in a lens? Well, ideally, all the beams should converge on a single point, rather than taking slightly different paths as they are doing now.</li>
+        
+        <li>Now we are going to fine-tune our lens by entering a value for hyperboloid curvature. Type "40" into the left "Hyperbolic curvature" window, and press Enter. If you have entered all the right values up to now, this should produce a nearly perfect focus -- all the beams should converge on a single point, located at roughly x = 5.4.</li>
+        <li>To discover how accurate this focus is, center the focal point in the display and zoom in on it (use your mouse wheel). Eventually you will get to a point where you can see some small imperfections in the focus. (This will become visible at a zoom factor of about 5.)</li>
+        <li>At this point you can fine-tune the lens' properties by carefully adjusting its hyperbolic curvature factor, which has an optimum near the value of 39. One way to make this adjustment is to position your mouse cursor over the hyperbolic curvature entry window and spinning the mouse wheel.</li>
+      </ul>
+      
+      <p>Hyperbolic-curvature lenses are an example of advanced optics and were once very difficult to manufacture. As computers come to play a greater role in optical manufacturing, these high-performance lenses should become more common.</p>
+      
+    </blockquote>
+    
+    <a name="Lens_Control_Panel"></a><div class="article_subtopic">Lens Control Panel</div>
+    
+    <blockquote>
+      
+      <p>Play with some of the settings in the lens control panel (the panel located on the "Design" tab) to see what effect they have. Notice that you can reposition a lens exactly by entering its x and y coordinates -- this is a way to get around the fact that it is difficult to position a lens precisely using the mouse.</p>
+      
+      <p>Notice the entry marked "IOR". This means "Index of Refraction," a value representing the ratio of the speed of light through the lens in question to a vacuum (which has an IOR of 1.0). If you set this value to 1.0, the lens will no longer deflect the light beams, because the lens has in essence been redefined as empty space.</p>
+      
+      <p>Different glasses have different indices of refraction, a property we can take advantage of in advanced lens designs. Here's an example design that displays the effect called dispersion using differently colored light beams.</p>
+      
+      <p>"Dispersion" is a property of glass in which light beams of different wavelengths travel at different speeds. For example, a blue beam takes longer to move through a lens than a red beam. This causes the two colors (wavelengths) of light to focus at two different places, a trait regarded as a bad thing, called "chromatic aberration."</p>
+      
+    </blockquote>
+    
+    <a name="Dispersion_Experiment"></a><div class="article_subtopic">Dispersion Experiment</div>
+    
+    <blockquote>
+      
+      <p>This is an optional digression for the curious. To set up for this experiment:</p>
+      
+      <ul>
+        <li>Delete any existing lenses.</li>
+        <li>Switch to the "Configuration" panel and enter "0" for "Snap-To-Grid Value" and "2" for "Light Beam Count".</li>
+        <li>Create a lens with these settings (or you can copy its definition from this page -- see below):
+          
+          <p><ul>
+              <li>Symmetrical: selected.</li>
+              <li>Lens Radius: 2.0</li>
+              <li>Left Sphere Radius: 5.0</li>
+              <li>Right Sphere Radius: 5.0</li>
+              <li>Lens Thickness: 0.0</li>
+              <li>Index of Refraction: 1.52</li>
+              <li>Abbe number: 59</li>
+              <li>X position: 0.0</li>
+              <li>Y position: 0.0</li>
+          </ul></p>
+          
+        </li>
+        
+        
+        <li><a href="file:/opticalraytracer/helpresources/dispersionTestLens1.txt">Click here ("Crown" glass lens)</a> to copy this lens definition onto the clipboard, then paste it into the experimental setup using the display context (right-click) menu (use "Paste: defined position").</li>
+        
+        <li>Now return to the ray trace display to see the effect.</li>
+        
+        <li>If all your settings are correct, and if the lens has really been positioned at x = 0, y = 0, the two beams should converge at about x = 3.7.</li>
+        
+      </ul>
+      
+      <p>Now we'll add a dispersion calculation.</p>
+      
+      <ul>
+        
+        <li>Go to the "Configuration" panel and enter "8" for "Dispersion beam count."</li>
+      </ul>
+      <p>When you return to the ray trace display, you should see an array of colored beams near the lens focal point. In this mode, OpticalRayTracer creates colored beams, each of which has an associated wavelength. During the calculation of the ray paths, the lens dispersion property is taken into account and, just as in the real world, the lens cannot focus all these wavelengths onto a single point.</p>
+      
+      <ul>
+        <li>Moving right along, create a second lens (or copy its definition from this page -- see below) with these properties:
+          
+          <p><ul>
+              <li>Symmetrical: deselected.</li>
+              <li>Lens Radius: 2.0</li>
+              <li>Left Sphere Radius: 10000</li>
+              <li>Right Sphere radius: -5</li>
+              <li>Lens Thickness: 0.3</li>
+              <li>Index of Refraction: 1.72</li>
+              <li>Abbe number: 29</li>
+              <li>X position: 0.347</li>
+              <li>Y position: 0.0</li>
+          </ul></p>
+          
+          
+        </li>
+        <li><a href="file:/opticalraytracer/helpresources/dispersionTestLens2.txt">Click here ("Flint" glass lens)</a> to copy this lens definition onto the clipboard, then paste it into the experimental setup using the display context (right-click) menu (use "Paste: defined position").</li>
+        
+        <li>If all the settings on both lenses are correct and all the other required settings have been made correctly, you will see all the colored beams converge at about x = 14.6, with <i>very little</i> color dispersion.</li>
+        
+        <li>It's possible to adjust the spacing between the lenses, which is both very sensitive and critical for the effect being modeled:
+          
+          <p> <ul>
+              <li>Select the concave lens, the lens at the right, by clicking it -- it will become green.</li>
+              <li>Pan over to the focal point (drag your mouse) and zoom in on the point where the beams cross (mouse wheel), choose a high magnification like 10 (readable on the status bar).</li>
+              <li>Select the Design tab and click the X Position entry window.</li>
+              <li>Now, while holding down the Shift and Alt keyboard keys (to greatly reduce the rate of change), spin your mouse wheel over the X Position window, and notice the effect on the focal point's position and quality.</li>
+              <li>This procedure tunes the spacing betweeen the lenses by a very small amount, to optimize the dispersion effect.</li>
+              
+          </ul></p>
+          
+        </li>
+        
+      </ul>
+      
+      <p>This, by the way, is a <a href="http://en.wikipedia.org/wiki/Achromatic_lens">classic solution</a> to the problem of chromatic aberration, using varieties of glass called "crown" and "flint," with differing properties that are exploited to make the light beams converge.</p>
+      
+      <p>By changing the spacing between the two lenses, you will quickly see that this setting is very critical to the outcome, which is why in the real world, such pairs of lenses are often glued together or placed in a lens cell with a spacer of some durable material to maintain the required separation.</p>
+      
+    </blockquote>
+    
+    <a name="Using_the_Mouse_and_Keyboard"></a><div class="article_subtopic">Using the Mouse and Keyboard</div>
+    
+    <blockquote>
+      
+      <p><b>Graphic display</b></p>
+      
+      <p>While playing with lens configurations, you may sometimes notice it is difficult to select a particular lens because the lenses are close together and their selection territories overlap. In a case like this, just click the display repeatedly -- the program will cycle through the lenses that could be selected at the location of your click. You also have the option of cycling among the objects by clicking the <img src="../icons/view-refresh.png" width="22" height="22" style="vert [...]
+      
+      <p>The graphic display pays attention to the mouse's various buttons and wheel, plus certain keyboard keys. Here's a list of mouse-related inputs and actions:</p>
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          
+          <tr>
+            <th>
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          
+          <tr>
+            <td>
+              Click once
+            </td>
+            <td>
+              Select an object near the mouse cursor
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Click more than once
+            </td>
+            <td>
+              Cycle through objects near mouse cursor
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Double-click
+            </td>
+            <td>
+              List properties of nearest line
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Drag mouse
+            </td>
+            <td>
+              Pan display
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Drag mouse with Shift or Ctrl keys
+            </td>
+            <td>
+              Move selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel
+            </td>
+            <td>
+              Zoom display
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel with Shift key
+            </td>
+            <td>
+              Rotate selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel with Ctrl key
+            </td>
+            <td>
+              Resize selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Most of the above actions with Alt key
+            </td>
+            <td>
+              Slower change
+            </td>
+          </tr>
+          
+        </table>
+        
+      </blockquote>
+      
+      <p>Here is a list of keyboard-related inputs and actions:</p>
+      
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          
+          <tr>
+            <th>
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          <tr>
+            <td>
+              Tab
+            </td>
+            <td>
+              Move forward through all program controls
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Shift|Tab
+            </td>
+            <td>
+              Move in reverse through all program controls
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Alt-D
+            </td>
+            <td>
+              Design tab
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Alt-C
+            </td>
+            <td>
+              Configure tab
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Alt-T
+            </td>
+            <td>
+              Table tab
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Alt-H
+            </td>
+            <td>
+              Help tab
+            </td>
+          </tr>
+          <tr>
+            <td>
+              F1
+            </td>
+            <td>
+              Concise help dialog
+            </td>
+          </tr>
+          <tr>
+            <td>
+              M or context menu key
+            </td>
+            <td>
+              Context [M]enu
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Enter (over object)
+            </td>
+            <td>
+              Select object under cursor
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Enter (outside objects)
+            </td>
+            <td>
+              List properties of nearest line
+            </td>
+          </tr>
+          <tr>
+            <td>
+              L
+            </td>
+            <td>
+              [L]ist properties of nearest line (even inside objects)
+            </td>
+          </tr>
+          <tr>
+            <td>
+              O
+            </td>
+            <td>
+              Cycle through [O]bject selections
+            </td>
+          </tr>
+          <tr>
+            <td>
+              U
+            </td>
+            <td>
+              [U]nselect all objects
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Up/down/left/right Arrow keys
+            </td>
+            <td>
+              Pan display
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Ctrl|Arrow or Shift|Arrow keys
+            </td>
+            <td>
+              Move selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              +/- or Home/End
+            </td>
+            <td>
+              Zoom display in/out
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Ctrl|(+/-) or Ctrl|(Home/End)
+            </td>
+            <td>
+              Resize selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Shift|(+/-) orShift|(Home/End)
+            </td>
+            <td>
+              Rotate selected object
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Most of the above actions with Alt key
+            </td>
+            <td>
+              Slower change
+            </td>
+          </tr>
+          
+        </table>
+        
+      </blockquote>
+      
+      
+      
+      
+      <p><b>Text Entry Fields</b></p>
+      
+      
+      <p>Virtually all OpticalRayTracer's text entry fields can be changed by placing the mouse cursor over them and spinning the mouse wheel. If the rate of change is too fast, hold down the shift key while spinning the mouse. If that rate is also too fast, hold down the shift and Alt keys together while spinning the mouse.</p>
+      
+      <p>
+        These actions can be gotten with some special keyboard keys also -- the up and down arrow keys will change the value by +1 and -1 respectively, with smaller changes if the shift and/or Alt keys are held down, just as with the mouse wheel example above. Here is a full list of these special controls:
+      </p>
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          
+          <tr>
+            <th>
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          
+          <tr>
+            <td>
+              Mouse wheel
+            </td>
+            <td>
+              Value increased/decreased by 1
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              up/down arrow keys
+            </td>
+            <td>
+              Value increased/decreased by 1
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Page Up/Page Down keys
+            </td>
+            
+            <td>
+              Value increased/decreased by 10
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Home/End keys
+            </td>
+            <td>
+              Value increased/decreased by 100
+            </td>
+            
+          </tr>
+          <tr>
+            <td>
+              Esc key
+            </td>
+            <td>
+              Change sign (+-) of associated entry
+            </td>
+            
+          </tr>
+        </table>
+        
+      </blockquote>
+      
+      <p>The text-field mouse-wheel and keyboard actions listed above can be modified by these accompanying keystrokes:
+      </p>
+      
+      <blockquote>
+        <table cellspacing="-1" cellpadding="4" class="bordered">
+          <tr>
+            <th>
+              
+              Action
+            </th>
+            <th>
+              Result
+            </th>
+          </tr>
+          <tr>
+            <td>
+              Wheel/keyboard with Shift key
+            </td>
+            <td>
+              Amount of change divided by 10
+            </td>
+          </tr>
+          
+          <tr>
+            <td>
+              Wheel/keyboard with Alt key
+            </td>
+            <td>
+              Amount of change divided by 100
+            </td>
+          </tr>
+          <tr>
+            <td>
+              Wheel/keyboard with both Shift and Alt keys
+            </td>
+            <td>
+              Amount of change divided by 1000
+            </td>
+          </tr>
+        </table>
+        
+      </blockquote>
+      
+      
+      
+    </blockquote>
+    
+    <a name="Importing_and_Exporting_Data"></a><div class="article_subtopic">Importing and Exporting Data</div>
+    
+    
+    <blockquote>
+      
+      <p>OpticalRayTracer has a number of methods for writing and reading data to/from the world at large, primarily by way of the system clipboard.</p>
+      
+      <ul>
+        <li>To create a copy of the specifications for a lens you've designed, simply click the lens, press the right mouse button and choose <img src="../icons/edit-copy.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy".</li>
+        
+        <li>To paste that lens somewhere else, move the mouse cursor to the desired destination point, press the right mouse button and select <img src="../icons/edit-paste.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Paste: mouse cursor."</li>
+        
+        <li>You can save lens descriptions in other locations, or even paste them into an e-mail for a friend, by pasting the lens description from the system clipboard. You can also make a copy of the entire experimental setup -- lenses, colors, zoom levels, everything -- by clicking the "Copy" button on the main toolbar below the graphic display (not the context-menu copy button). This places a full description of OpticalRayTracer's present state -- all the lenses and mirrors, plus pro [...]
+        
+        <li>To make a graphic copy of the workspace display, click the <img src="../icons/applications-multimedia.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy Workspace" toolbar button, then open a graphic image editor and choose "Paste".</li>
+        
+        <li><p>To create a plain-text table of all the generated lines and place it on the system clipboard, click the <img src="../icons/document-save.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> "Copy Table" toolbar button. The generated table can be easily imported into databases and spreadsheets. Note also that you can get information about individual lines by double-clicking the display near the line of interest. You can also use the line properties o [...]
+          
+          
+          <p>Here's an example of an exported data table generated for a simple Newtonian reflector with three components -- a primary mirror, a diagonal reflector, and an eyepiece lens, all named for clarity:</p>
+          
+          
+          <blockquote>
+            <table cellspacing="-1" cellpadding="4" class="bordered">
+              
+              <tr><th>From</th><th>To</th><th>Source</th><th>Destination</th><th>DestinationType</th><th>FromX</th><th>FromY</th><th>ToX</th><th>ToY</th><th>DeltaX</th><th>DeltaY</th><th>Magnitude</th><th>BeamAngle</th><th>SurfaceNormalAngle</th><th>WavelengthNM</th></tr>
+              <tr><td class = "lj">Beam Origin</td><td class = "lj">Reflection</td><td class = "lj">Origin Ray 1</td><td class = "lj">Primary Mirror</td><td class = "lj">Mirror</td><td >-30.0000</td><td >-1.8000</td><td >7.7244</td><td >-1.8000</td><td >37.7244</td><td >2.2204e-16</td><td >37.7244</td><td >3.3724e-16</td><td >173.4166</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Reflection</td><td class = "lj">Reflection</td><td class = "lj">Primary Mirror</td><td class = "lj">Diagonal Mirror</td><td class = "lj">Mirror</td><td >7.7244</td><td >-1.8000</td><td >1.2962</td><td >-0.2962</td><td >-6.4282</td><td >1.5038</td><td >6.6017</td><td >166.8332</td><td >-315.0002</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Reflection</td><td class = "lj">Refraction</td><td class = "lj">Diagonal Mirror</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Lens</td><td >1.2962</td><td >-0.2962</td><td >0.6519</td><td >2.4582</td><td >-0.6443</td><td >2.7544</td><td >2.8288</td><td >103.1664</td><td >-102.5668</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Refraction</td><td class = "lj">Refraction</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Lens</td><td >0.6519</td><td >2.4582</td><td >0.6461</td><td >2.5405</td><td >-0.0058</td><td >0.0823</td><td >0.0825</td><td >94.0306</td><td >-77.2203</td><td >589.3000</td></tr>
+              <tr><td class = "lj">Refraction</td><td class = "lj">Termination</td><td class = "lj">Eyepiece Lens</td><td class = "lj">Virtual space boundary</td><td class = "lj">Domain Limit</td><td >0.6461</td><td >2.5405</td><td >1.6467</td><td >100.0000</td><td >1.0006</td><td >97.4595</td><td >97.4646</td><td >89.4118</td><td >0.0000e+00</td><td >589.3000</td></tr>
+            </table>
+            
+            
+            
+            
+            
+            
+          </blockquote>
+          
+          
+          
+          
+        </li>
+        
+      </ul>
+    </blockquote>
+    
+    <a name="System_Considerations"></a><div class="article_subtopic">System Considerations</div>
+    
+    <blockquote>
+      
+      <p>Remember that OpticalRayTracer is a Java application, which means it needs a current Java runtime engine. If the behavior of your copy of OpticalRayTracer doesn't correspond with the description provided here, it probably means your installed version of Java is not up-to-date. To remedy this, visit <a href="http://java.com">http://java.com</a> to update your Java installation (Java is free).</p>
+      
+      <p>Remember also that the total number of beams traced is equal to the number of tracing beams (selected in the "Configuration" panel) multiplied by the number of dispersion beams, e. g. there is a dispersion beam for each chosen wavelength, times each tracing beam. So if the display slows down, this could easily be the reason -- too many beams selected. To prevent calculation of dispersion, simply set "Dispersion Beam Count" to zero.</p>
+      
+    </blockquote>
+    
+    <a name="Algorithm_Description"></a><div class="article_subtopic">Algorithm Description</div>
+    
+    <blockquote>
+      
+      (For a more complete presentation of this topic, visit the <a href="http://arachnoid.com/OpticalRayTracer_technical/index.html">OpticalRayTracer technical discussion page</a>.)
+      
+      <p>OpticalRayTracer first calculates the location of any intersections between tracing beams and spheres or hyperboloids (our lenses). The collision detection mathematics is rather involved and won't be described in any detail here.</p>
+      
+      <p>Having acquired a list of all possible points of collision for a particular beam, OpticalRayTracer sorts the list of results along the current direction, then determines which intersection is closest along the beam's path.</p>
+      
+      <p>At this point OpticalRayTracer has determined a point of collision between a tracing ray and a lens or mirror. The ray and the lens collision point each have a characteristic angle, which is used in the next computation.</p>
+      
+    </blockquote>
+    
+    <a name="Snell_s_Law"></a><div class="article_subtopic">Snell's Law</div>
+    
+    <blockquote>
+      <p>"Snell's Law" is a classic optical relationship that, given arguments for incidence angle between two media and indices of refraction for the two media, determines the deflection angle. Expressed in classic form, Snell's Law is:</p>
+      
+      <blockquote>
+        n1 sin(a1) = n2 sin(a2)
+      </blockquote>
+      
+      <p>Where:</p>
+      
+      <ul>
+        <li>n1 = index of refraction of medium 1</li>
+        <li>a1 = angle within medium 1</li>
+        
+        <li>n2 = index of refraction of medium 2</li>
+        <li>a2 = angle within medium 2</li>
+      </ul>
+      
+      <p>The astute reader will notice that, in passing from a medium like air with an IOR near 1.0, to a lens with an IOR of 1.5 for example, the light beam's angle with repect to the surface normal must decrease. And conversely, a beam emerging from glass to air will show an increase in its angle of deflection. It can also be seen that an incident relative angle of zero will not be deflected -- it will remain zero.</p>
+      
+      <p>In computing refraction, OpticalRayTracer uses this restatement of the classic Snell's Law equation:</p>
+      
+      <blockquote>
+        a2 = sin<sup>-1</sup>(sin(a1) n1 / n2)
+      </blockquote>
+      
+      <p>Here's a practical example:</p>
+      
+      
+      
+      <ul>
+        <li>Medium 1: air
+          
+          <ul>
+            <li>n1 = 1.0</li>
+            <li>a1 = 20 degrees</li>
+          </ul>
+        </li>
+        <li>Medium 2: crown spectacle glass
+          
+          <ul>
+            <li>n2 = 1.52</li>
+            <li>a2 = sin<sup>-1</sup>(sin(a1) n1 / n2) = 13.00365 degrees</li>
+          </ul>
+        </li>
+      </ul>
+      
+      
+      <p>The above example (in which n1 < n2) causes the beam to deflect toward the normal line (the line perpendicular to the optical surface). In the reverse case (n1 > n2), the ray is deflected away from the normal line. In some cases this may exceed a "critical angle" such that the beam is deflected back into the refracting medium. This is called "total internal reflection", and in such a case OpticalRayTracer imitates nature by reflecting the beam back into the medium using a  [...]
+      
+      <p>Note: OpticalRayTracer provides very reliable Snell's Law results, as accurate as the entered lens measurements. The values listed in the OpticalRaytracer data table can be relied on for optical analysis purposes within floating-point processing accuracy limitations.</p>
+      
+    </blockquote>
+    
+    <a name="Dispersion_Computation"></a><div class="article_subtopic">Dispersion Computation</div>
+    
+    <blockquote>
+      <p>The computation for dispersion follows along similar lines, but this task is less open to analysis from physically simple principles. My empirical dispersion equation changes the index of refraction based on the wavelength of the light beam:</p>
+      
+      
+      
+      <blockquote>
+        ior' = ior + ((dp - w) 5x10<sup>5</sup>) / (abbe dp w<sup>2</sup>)
+      </blockquote>
+      
+      <p>Where:</p>
+      
+      
+      
+      <ul>
+        <li>ior' = effective index of refraction at wavelength w</li>
+        
+        <li>ior = default index of refraction for the medium.</li>
+        
+        <li>dp = dispersion pivot wavelength, set to 589.3 nm, the sodium yellow line.</li>
+        
+        <li>w = wavelength of the tracing beam in nanometers.</li>
+        
+        <li>abbe = Abbe's Number, a value published for many glasses that describes its dispersion property. Lower Abbe's numbers result in higher dispersion.</li>
+      </ul>
+      
+      <p>Abbe's Number is arrived at in this way:</p>
+      
+      <blockquote>
+        abbe = (nd-1)/(nf-nc)
+      </blockquote>
+      
+      <p>Where:</p>
+      
+      <ul>
+        <li>nf = a medium's index of refraction at the 486.1 nm hydrogen blue line.</li>
+        <li>nd = a medium's index of refraction at the 589.3 nm sodium yellow line.</li>
+        <li>nc = a medium's index of refraction at the 656.3 nm hydrogen red line.</li>
+      </ul>
+      
+      <p>Abbe numbers for various media are arrived at empirically in laboratory experiments. My equation reverses the relationship between the number and its effects, giving a dispersion-modified IOR with an accuracy of about 5% for many common glasses.</p>
+      
+      <p>Note: OpticalRayTracer provides <i>approximate</i> dispersion results, suitable for graphic display purposes but not precise (as explained <a href="http://arachnoid.com/OpticalRayTracer_technical/index.html#h14">here</a>). If very accurate results are required, a formal calculation is recommended.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Design_Options"></a><div class="article_subtopic">Design Options</div>
+    
+    <blockquote>
+      
+      <p>Here is a list of the <img src="../icons/applications-graphics.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> <b>Design</b> tab options and their meanings.</p>
+      
+      <ul>
+        <li>The <b>Name</b> field acceps a custom name for each optical component. A default name is provided, but for complex optical setups, this feature is a nice way to keep track of components. Also, when a table of tracing data is exported, the names are included to help clarify what might otherwise be a confusing list of numerical data.</li>
+        
+        <li>The <b>Symmetrical</b> checkbox specifies that the lens is symmetrical -- the left and right sides are the same. This simplifies configuration for a symmetrical lens becaue only one entry needs to be made for each property.</li>
+        
+        <li>The <b>Radius</b> entry specifies the distance between the center of a lens and its edge. This value is sometimes confused with the left and right Sphere Radius entries discussed below. The difference is that the sphere radii specify two imaginary spheres that construct the lens, while this entry specifies the lens size from center to edge.</li>
+        
+        <li>The <b>Thickness</b> entry specifies the left-to-right thickness of the main body of the lens, apart from its curvature. This entry allows lenses to have a thickness beyond their optical curvature, and is typical of real-world lenses.</li>
+        
+        <li>The <b>X and Y Position</b> entries specify the lens placement in the X (horizontal) and Y (vertical) dimensions. The user can also drag the mouse cursor on the lens image to change these entries.</li>
+        
+        
+        <li>There are two entries, left and right, for these values:
+          
+          <p><ul>
+              <li>The <b>Sphere Radius</b> entry specifies the radius of one of the two spheres from which this lens is constructed. See the explanation at the <a href="http://arachnoid.com/OpticalRayTracer_technical/index.html">OpticalRayTracer Technical Description</a> page.</li>
+              
+              <li>The <b>Curvature</b> text entry field accepts an entry for hyperbolic curve:
+                <ul>
+                  <li>Briefly, the curvature factor represents the location of a plane that bisects a unit cone (a hyperbola can be thought of as a <a href="http://en.wikipedia.org/wiki/Conic_section">conic section</a>).</li>
+                  <li>A factor entry of zero positions the bisecting plane at the cone's apex, which produces a lens with a triangular profile, essentially a prism.</li>
+                  <li>Larger factor entries move away from the cone's apex and produce less extreme hyperbolic curvatures, and very large numbers (i.e. 1 x 10<sup>7</sup>) change the curvature from a hyperbola to a figure approximating a parabola.</li>
+                  <li>For tasks that require hyperbolic curvature, entries from 0 to about 100 represent a range of useful shapes.</li>
+                  <li>For an approximate parabolic curve, for example to design a telescope mirror, enter a number around  1 x 10<sup>7</sup>, the actual numeric value isn't as important as its size, since mathematically speaking, there's only one parabolic curve. Then adjust the size and focal length of the optical element with the usual controls.</li>
+                </ul>
+                
+                
+              </li>
+              
+              <li>The next entry option consists of three "radio buttons" that selects one of the available optical surface curvatures for each side of a lens or mirror -- <b>Spherical</b>, <b>Parabolic</b>, and <b>Hyperbolic</b>, details of which are discussed above.</li>
+              
+          </ul></p>
+          
+          
+          <li>The <b>IOR</b> checkbox specifies the lens material's <a href="http://en.wikipedia.org/wiki/Refractive_index">index of refraction</a>, which is the ratio of the speed of light in the lens material compared to that in a vacuum.</li>
+          
+          <li>The <b>Dispersion</b> entry specifies the amount of <a href="http://en.wikipedia.org/wiki/Dispersion_(optics)">dispersion</a> (a wavelength-dependent property) the lens material possesses.</li>
+          
+          <li>The <b>Angle</b> entry rotates the lens or mirror in the X/Y plane. This is useful for evaluating the off-axis behavior of a lens, and for setting up complex optical configurations, for example by directing beams using angled mirrors. </li>
+          <li>The <b>Active</b> checkbox includes or excludes its associated component in the optical calculation. This is a convenient way to manage complex optical models and interactions -- using this feature one can place multiple components in the same location or path and easily switch between them.</li>
+          
+          <li>As explained above, OpticalRayTracer supports three kinds of optical objects: Refractors, Reflectors and Absorbers:
+            <ul>
+              <li>The <b>Refract</b> option is the default choice for lenses. Rays that intersect with this object will be refracted using the outcome of a Snell's Law calculation.</li>
+              <li>The <b>Reflect</b> option turns a lens into a mirror. Light beams reflect from the lens' surfaces instead of passing through. This option makes it possible to create optical designs with mirrors, including curved mirrors such as one finds in modern telescopes.</li>
+              <li>The <b>Absorb</b> option causes intersecting rays to be absorbed and terminated. This is a convenient way to create a barrier against certain rays that would interfere with a desired result. As with lenses and mirrors, an absorber can be given any desired size or shape.</li>
+            </ul>
+          </li>
+          
+          
+        </li>
+        
+        
+      </ul>
+      
+      
+      
+      
+    </blockquote>
+    
+    <a name="Configuration_Options"></a><div class="article_subtopic">Configuration Options</div>
+    
+    <blockquote>
+      
+      <p>Here is an explanation of the controls in the <img src="../icons/applications-accessories.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> <b>Configure</b> tab:</p>
+      
+      <ul>
+        <li>First, the <b>Color</b> button bar:
+          <p><ul>
+              <li>To change a color in the  <b>Configure</b> tab's selection list, simply click the colored button for the value you want to change (each button has a flyout explanation that appears when you hover the mouse cursor over the button). A color selection dialog will appear.</li>
+              
+              <li>OpticalRayTracer supports color transparency, which means if you adjust the "Alpha" setting in the color selection dialog, OpticalRaytracer will honor your choice and make the associated color transparent in proportion (in this context, "Alpha" means transparency). This is useful in a number of situations, for example when light beams overlap and obscure each other -- when the light beams are transparent, it's easier to find a true focal point.</li>
+              
+              <li><b>Light Source Bar Color</b>. This is the color of the vertical line that marks the light beam origin.</li>
+              
+              <li><b>X/Y Zero Baseline Color</b>. This is the color of the reference lines that appear at x = 0 and y = 0 in the ray trace display. If you do not want these lines to appear, set their color to the color of the display background (see "Display Background" below).</li>
+              
+              <li><b>Grid Color</b>. This is the auto-scaling grid in the ray trace display. Again, you can turn this off by setting its color to that of the background.</li>
+              
+              <li><b>Lens</b> color. This color is used to draw the profile and interior of each lens when not selected.</li>
+              
+              <li><b>Tracing Beams Color</b>. This is the beam color used when dispersion is not being computed. Remember that dispersion beams have an internally computed color, appropriate to their wavelength.</li>
+              
+              <li><b>Beam Intersection Color</b>. This is the color of the arrows that mark the intersections between light beams and lens surfaces, where beams change direction by way of refraction.</li>
+              
+              <li><b>Selected Lens Color</b>. A lens changes to this color when it has been selected for editing, either by clicking the lens directly or by way of the cycle lens toolbar button.</li>
+              
+              <li><b>Display High Background Color</b>. This color defaults to white, but in some cases, like trying to make out the color of dispersion beams, another color might be better.</li>
+              
+              <li><b>Display Low Background Color</b>. This color defaults to black, but the user has the option of choosing a different color.</li>
+          </ul></p>
+          
+        </li>
+        
+        <li><b>Intersection Arrow Size</b>. This numeric entry specifies the size of the arrows that mark the destination points of light beams. To remove the arrows from the display, set this value to zero.</li>
+        
+        <li><b>Snap to grid value</b>. This numeric entry sets the threshold for the behavior that returns a lens to the X and Y grid when the mouse is released. This is ordinarily a good thing, a feature, but if you prefer this not to happen, set this number to zero to disable the feature, or choose a different value for special purposes.</li>
+        
+        <li><b>Beam width</b>. This setting adjusts the width of all image lines. An entry of zero is ignored, the minimum beam width is 1.</li>
+        
+        <li><b>Light beam count</b>. This is a setting with a large impact on program performance. OpticalRayTracer's algorithms are swift, but choosing a large number of tracing beams might disable one of its best features: real-time response to user inputs. Also, it is easy to become overloaded with information as the number of beams increases. It is difficult to take advantage of the information presented by more than about 8 tracing beams. Lots of beams can be entertaining, though.</li>
+        
+        <li><b>Source Y start and end</b>. These values determine the vertical limits for the array of tracing beams. These two numbers are typically set to fall within the chosen radius of your lenses. The default settings of -1.8 and 1.8 means all the beams fall comfortably within the default lens radius of 2 units (remember that a lens diameter is twice its radius).</li>
+        
+        <li><b>Maximum interactions</b>. This determines the limit to interactions for a given beam, to avoid the computation of pointless internal reflections within a lens, for example. The default value is quite large, but a sufficiently complex lens system might require that it be increased.</li>
+        
+        <li><b>X Source plane</b>. This is the location in the x dimension from which the tracing beams emanate. In the ray trace display, zoom out to see the location of the beam origin, then change this number to see what happens.</li>
+        
+        <li><b>X beam rotation plane</b>. This is the plane in the x dimension that represents the pivot point for beam rotation. If an entry is made for beam offset angle below, this will be the plane around which the beams will rotate. The default is zero, and for a specific off-axis lens test, the user has the option of moving the lens to this location, changing this defined plane to be that of the lens, or rotating the lens instead of the beams.</li>
+        
+        <li><b>Beam offset angle</b>. This setting is used to tilt the array of tracing beams, a way to test the off-axis performance of a lens without having to rotate the lens itself. By controlling the source angle you can observe the resulting path through a series of lenses, thus determining the off-axis performance for the entire system.</li>
+        
+        <li><b>Dispersion beam count</b>. This setting produces a set of dispersion beams, with appropriate colors and wavelengths, for each tracing beam. These dispersion beams will be deflected by different amounts depending on specific lens settings, in particular the dispersion value. This is the key to the dispersion effect simulation.</li>
+        
+        <li><b>Interlens epsilon</b>. This setting creates a threshold to distinguish between optical surfaces. If during calculation two surfaces are found to be separated by a small distance, OpticalRayTracer may assume the source and destination are the same surface -- and that may be true. To prevent this from happening, increase this setting. Most OpticalRayTracer numerical entries are large, but this one is normally very small, such that scientific notation is normally used, for ex [...]
+        
+        <li><b>Surface epsilon</b>. This value creates an acceptance boundary around optical objects so the ray tracer can identify and interact with them. If a light ray skips over a legitimate object instead of interacting with it, increase this value. If light rays seem to be interacting with the empty space near lenses, decrease this value. In nearly all cases this setting's default value shouldn't need to be changed.</li>
+        
+        <li><b>Virtual space box size</b>. This is an outer limit size for the OpticalRayTracer virtual space. This value prevents anomalous behavior that can result from overly large computation values in the absence of limits.</li>
+        
+        <li><b>Diverging Beams</b>. This checkbox causes the tracing beams to originate in and diverge from a point source located at the X source reference plane, rather than being generated in parallel at that plane.</li>
+      </ul>
+      
+      <p>Remember that all the above values are reset by the <img src="../icons/process-stop.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> Erase & Reset button. If you make settings that cause problems, simply reset the program to its default values.</p>
+      
+      
+      <p>Remember also that the total number of ray trace computations is equal to the number of tracing beams multiplied by the number of dispersion beams, such that choosing 8 tracing beams and 8 dispersion beams results in 64 traces, fine for a fast computer, but not so great for a slower machine. To prevent the generation of dispersion beams and their associated computation overhead, set this value to zero.</p>
+      
+      
+      <p>All the configuration values, along with a full description of the lenses you create, are preserved between sessions in a file located at #userdir#/OpticalRayTracer.ini on this system, and the same information can be copied onto the system clipboard with the toolbar "Copy Configuration" button.</p>
+      
+      
+    </blockquote>
+    
+    <a name="Introduction"></a><div class="article_subtopic">Common Problems</div>
+    
+    
+    
+    <ul>
+      <li>When designing systems of lenses, be sure that your lenses do not overlap, that one lens is not located within another, and that the light source is not located within a lens. In short, <i>the light source must be separate from the lenses, and and the lenses must be separate from each other</i>.</li>
+      <li>If light beams pass through lens surfaces without any interaction, this may result from a <b>Surface Epsilon</b> value (on the <img src="../icons/applications-accessories.png" width="22" height="22" style="vertical-align:middle;" title="" alt=""/> Configure tab) that is too small for the scale of the lenses -- try increasing this value.</li>
+    </ul>
+    
+    
+    
+    <a name="Conclusion"></a><div class="article_subtopic">Conclusion</div>
+    
+    <blockquote>
+      <p>The OpticalRayTracer Home Page is located at <a href="http://www.arachnoid.com/OpticalRayTracer">http://www.arachnoid.com/OpticalRayTracer</a>, where additional documentation and other resources are located. Be sure to visit to make sure you have the latest version of OpticalRayTracer.</p>
+      
+      
+      
+      <p>There is a great deal of excellent, detailed information about optics on the Web, both theoretical and practical. Google for "optics," "ray tracing" and related topics.</p>
+      
+      
+    </blockquote>
+    
+    <a name="User_support"></a><div class="article_subtopic">User support</div>
+    
+    <blockquote>
+      
+      <p><a href="http://arachnoid.com/OpticalRayTracer_technical/index.html">Click here</a> for a more detailed technical description of OpticalRayTracer and optical mathematics in general.</p>
+      
+      <p>Because OpticalRayTracer is released under the <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> (but please visit <a href="http://www.arachnoid.com/careware">Careware</a> anyway), there is no user support. This help file plus the sort of knowledge available in optical textbooks and online should be sufficient to help the user make it productive.</p>
+      
+      <p>If you detect a bug in OpticalRayTracer, please report it at <a href="http://www.arachnoid.com/messages">arachnoid.com Messages</a>.</p>
+      
+      
+    </blockquote>
+  </body>
+</html>
diff --git a/src/opticalraytracer/helpresources/dispersionTestLens1.txt b/src/opticalraytracer/helpresources/dispersionTestLens1.txt
new file mode 100644
index 0000000..4e95bbb
--- /dev/null
+++ b/src/opticalraytracer/helpresources/dispersionTestLens1.txt
@@ -0,0 +1,20 @@
+object {
+  active                    = true
+  angle                     = 0.000000e+00
+  centerThickness           = 0.834849
+  dispersion                = 59.000000
+  function                  = 0
+  ior                       = 1.520000
+  leftCurvature             = 0
+  leftSphereRadius          = 5.000000
+  leftZValue                = 20.000000
+  lensRadius                = 2.000000
+  name                      = Crown lens
+  rightCurvature            = 0
+  rightSphereRadius         = 5.000000
+  rightZValue               = 20.000000
+  symmetrical               = true
+  thickness                 = 0.000000e+00
+  xPos                      = 0.000000e+00
+  yPos                      = 0.000000e+00
+}
diff --git a/src/opticalraytracer/helpresources/dispersionTestLens2.txt b/src/opticalraytracer/helpresources/dispersionTestLens2.txt
new file mode 100644
index 0000000..5e90640
--- /dev/null
+++ b/src/opticalraytracer/helpresources/dispersionTestLens2.txt
@@ -0,0 +1,20 @@
+object {
+  active                    = true
+  angle                     = 0.000000e+00
+  centerThickness           = 0.182576
+  dispersion                = 29.000000
+  function                  = 0
+  ior                       = 1.720000
+  leftCurvature             = 3
+  leftSphereRadius          = 10000.000000
+  leftZValue                = 20.000000
+  lensRadius                = 2.000000
+  name                      = Flint lens
+  rightCurvature            = 0
+  rightSphereRadius         = -5.000000
+  rightZValue               = 20.000000
+  symmetrical               = false
+  thickness                 = 0.600000
+  xPos                      = 0.381000
+  yPos                      = 0.000000e+00
+}
diff --git a/src/opticalraytracer/helpresources/dispersionTestSetup.txt b/src/opticalraytracer/helpresources/dispersionTestSetup.txt
new file mode 100644
index 0000000..903db4d
--- /dev/null
+++ b/src/opticalraytracer/helpresources/dispersionTestSetup.txt
@@ -0,0 +1,113 @@
+# OpticalRayTracer 8.8
+# http://arachnoid.com/OpticalRayTracer
+# 2015.02.05 00:04:11 PST
+
+program {
+  antialias                 = true
+  askBeforeDeleting         = true
+  beamAngle                 = 0.000000e+00
+  beamCount                 = 4
+  beamWidth                 = 1
+  clipboardGraphicXSize     = 1280
+  colorArrow                = -2147483393
+  colorBaseline             = -16760832
+  colorBeam                 = -4194304
+  colorGrid                 = 1086374080
+  colorHighBackground       = -1
+  colorLensOutline          = 276865279
+  colorLensSelected         = 268484608
+  colorLightSource          = -16776961
+  colorLowBackground        = -16777216
+  colorTerminator           = -16777216
+  decimalPlaces             = 4
+  defaultWindowHeight       = 708
+  defaultWindowWidth        = 900
+  dispScale                 = 0.099735
+  dispersionBeams           = 8
+  divergingSource           = false
+  helpScrollPos             = 4918
+  interLensEpsilon          = 1.000000e-06
+  intersectionArrowSize     = 0.050000
+  inverse                   = false
+  maxIntersections          = 64
+  selectedComponent         = 2
+  selectedTab               = 0
+  showControls              = true
+  showGrid                  = true
+  snapValue                 = 0.000000e+00
+  surfEpsilon               = 5.000000e-04
+  tableLineLimit            = 500
+  virtualSpaceSize          = 100.000000
+  windowX                   = 129
+  windowY                   = 38
+  xBeamRotationPlane        = 0.000000e+00
+  xBeamSourceRefPlane       = -30.000000
+  xOffset                   = 7.358362
+  yEndBeamPos               = 1.800000
+  yOffset                   = 0.053731
+  yStartBeamPos             = -1.800000
+}
+
+object {
+  active                    = true
+  angle                     = 0.000000e+00
+  centerThickness           = 0.100000
+  dispersion                = 59.000000
+  function                  = 2
+  ior                       = 1.520000
+  leftCurvature             = 3
+  leftSphereRadius          = 10.000000
+  leftZValue                = 20.000000
+  lensRadius                = 10.000000
+  name                      = Terminal Plane
+  rightCurvature            = 3
+  rightSphereRadius         = 10.000000
+  rightZValue               = 20.000000
+  symmetrical               = true
+  thickness                 = 0.100000
+  xPos                      = 30.000000
+  yPos                      = 0.000000e+00
+}
+
+object {
+  active                    = true
+  angle                     = 0.000000e+00
+  centerThickness           = 0.834849
+  dispersion                = 59.000000
+  function                  = 0
+  ior                       = 1.520000
+  leftCurvature             = 0
+  leftSphereRadius          = 5.000000
+  leftZValue                = 20.000000
+  lensRadius                = 2.000000
+  name                      = Crown lens
+  rightCurvature            = 0
+  rightSphereRadius         = 5.000000
+  rightZValue               = 20.000000
+  symmetrical               = true
+  thickness                 = 0.000000e+00
+  xPos                      = 0.000000e+00
+  yPos                      = 0.000000e+00
+}
+
+object {
+  active                    = true
+  angle                     = 0.000000e+00
+  centerThickness           = 0.182576
+  dispersion                = 29.000000
+  function                  = 0
+  ior                       = 1.720000
+  leftCurvature             = 3
+  leftSphereRadius          = 10000.000000
+  leftZValue                = 20.000000
+  lensRadius                = 2.000000
+  name                      = Flint lens
+  rightCurvature            = 0
+  rightSphereRadius         = -5.000000
+  rightZValue               = 20.000000
+  symmetrical               = false
+  thickness                 = 0.600000
+  xPos                      = 0.381000
+  yPos                      = 0.000000e+00
+}
+
diff --git a/src/opticalraytracer/helpresources/overlapDiagram.png b/src/opticalraytracer/helpresources/overlapDiagram.png
new file mode 100644
index 0000000..1c30a6a
Binary files /dev/null and b/src/opticalraytracer/helpresources/overlapDiagram.png differ
diff --git a/src/opticalraytracer/icons/OpticalRayTracer.png b/src/opticalraytracer/icons/OpticalRayTracer.png
index d390254..6524b7d 100644
Binary files a/src/opticalraytracer/icons/OpticalRayTracer.png and b/src/opticalraytracer/icons/OpticalRayTracer.png differ

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/opticalraytracer.git



More information about the debian-science-commits mailing list