Merge branch 'Jalview-JS/develop' into merge_js_develop
authorJim Procter <jprocter@issues.jalview.org>
Mon, 14 Dec 2020 19:58:34 +0000 (19:58 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Mon, 14 Dec 2020 20:28:39 +0000 (20:28 +0000)
also patched new code from JAL-3690 refactorings

314 files changed:
.gitignore
THIRDPARTYLIBS
build-libjs.xml [new file with mode: 0644]
build.gradle
doc/JalviewJS-API.md [new file with mode: 0644]
doc/JalviewJS-startupParams.md [new file with mode: 0644]
doc/JalviewJS-startupParams.xlsx [new file with mode: 0644]
doc/building.html
doc/building.md
doc/releaseprocess.html [new file with mode: 0644]
gradle.properties
help/help/html/releases.html
j11lib/Jmol-14.29.17-no_netscape.jar [deleted file]
j11lib/Jmol-15.1.3.jar [new file with mode: 0644]
j11lib/intervalstore-v1.0.jar [deleted file]
j11lib/intervalstore-v1.1.jar [new file with mode: 0644]
j8lib/Jmol-14.29.17.jar [deleted file]
j8lib/Jmol-15.1.3.jar [new file with mode: 0644]
j8lib/intervalstore-v1.0.jar [deleted file]
j8lib/intervalstore-v1.1.jar [new file with mode: 0644]
src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java
src/jalview/analysis/AlignmentSorter.java
src/jalview/analysis/CrossRef.java
src/jalview/analysis/Dna.java
src/jalview/analysis/Finder.java
src/jalview/analysis/ParseProperties.java
src/jalview/analysis/scoremodels/ScoreModels.java
src/jalview/api/JalviewJSApi.java [new file with mode: 0644]
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/appletgui/RedundancyPanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/TitledPanel.java
src/jalview/appletgui/TreeCanvas.java
src/jalview/appletgui/js/JSFunctionExec.java [moved from src/jalview/javascript/JSFunctionExec.java with 99% similarity]
src/jalview/appletgui/js/JalviewLiteJsApi.java [moved from src/jalview/javascript/JalviewLiteJsApi.java with 99% similarity]
src/jalview/appletgui/js/JsCallBack.java [moved from src/jalview/javascript/JsCallBack.java with 97% similarity]
src/jalview/appletgui/js/JsSelectionSender.java [moved from src/jalview/javascript/JsSelectionSender.java with 99% similarity]
src/jalview/appletgui/js/MouseOverListener.java [moved from src/jalview/javascript/MouseOverListener.java with 99% similarity]
src/jalview/appletgui/js/MouseOverStructureListener.java [moved from src/jalview/javascript/MouseOverStructureListener.java with 99% similarity]
src/jalview/bin/AppletParams.java [new file with mode: 0644]
src/jalview/bin/ApplicationSingletonProvider.java [new file with mode: 0644]
src/jalview/bin/ArgsParser.java
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/bin/JalviewJS2.java
src/jalview/bin/JalviewJSApp.java [new file with mode: 0644]
src/jalview/bin/JalviewLite.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/PDBEntry.java
src/jalview/datamodel/features/FeatureAttributes.java
src/jalview/datamodel/features/FeatureSources.java
src/jalview/datamodel/features/FeatureStore.java
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/ext/ensembl/EnsemblCdna.java
src/jalview/ext/ensembl/EnsemblCds.java
src/jalview/ext/ensembl/EnsemblFeatures.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/ext/ensembl/EnsemblInfo.java
src/jalview/ext/ensembl/EnsemblProtein.java
src/jalview/ext/ensembl/EnsemblSeqProxy.java
src/jalview/ext/ensembl/EnsemblSequenceFetcher.java
src/jalview/ext/ensembl/EnsemblXref.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/so/SequenceOntology.java
src/jalview/fts/service/pdb/PDBFTSRestClient.java
src/jalview/fts/service/uniprot/UniProtFTSRestClient.java
src/jalview/gui/AlignExportOptions.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationChooser.java
src/jalview/gui/AnnotationColourChooser.java
src/jalview/gui/AnnotationExporter.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AssociatePdbFileWithSeq.java
src/jalview/gui/CalculationChooser.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/ColourMenuHelper.java
src/jalview/gui/CrossRefAction.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureEditor.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Finder.java
src/jalview/gui/FontChooser.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/JalviewDialog.java
src/jalview/gui/JvSwingUtils.java
src/jalview/gui/LineartOptions.java
src/jalview/gui/OOMWarning.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/PCAPanel.java
src/jalview/gui/PaintRefresher.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/PromptUserConfig.java
src/jalview/gui/RedundancyPanel.java
src/jalview/gui/RestInputParamEditDialog.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/SliderPanel.java
src/jalview/gui/SplashScreen.java
src/jalview/gui/SplitFrame.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/StructureViewer.java
src/jalview/gui/StructureViewerBase.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java
src/jalview/gui/UserDefinedColours.java
src/jalview/gui/UserQuestionnaireCheck.java
src/jalview/gui/VamsasApplication.java
src/jalview/gui/WebserviceInfo.java
src/jalview/gui/WsJobParameters.java
src/jalview/gui/WsParamSetManager.java
src/jalview/gui/WsPreferences.java
src/jalview/httpserver/HttpServer.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/BSMLFile.java [new file with mode: 0644]
src/jalview/io/BackupFiles.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/CountReader.java
src/jalview/io/FileFormat.java
src/jalview/io/FileFormats.java
src/jalview/io/FileLoader.java
src/jalview/io/IdentifyFile.java
src/jalview/io/ModellerDescription.java
src/jalview/io/NewickFile.java
src/jalview/io/RnamlFile.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/io/StockholmFile.java
src/jalview/io/VamsasAppDatastore.java
src/jalview/io/WSWUBlastClient.java
src/jalview/io/cache/AppCache.java
src/jalview/io/gff/Gff3Helper.java
src/jalview/io/gff/InterProScanHelper.java
src/jalview/io/gff/SequenceOntologyFactory.java
src/jalview/io/packed/ParsePackedSet.java
src/jalview/io/vamsas/Sequencemapping.java
src/jalview/javascript/log4j/Logger.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
src/jalview/jbgui/GCutAndPasteTransfer.java
src/jalview/jbgui/GDesktop.java
src/jalview/jbgui/GPCAPanel.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GSequenceLink.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/jbgui/GTreePanel.java
src/jalview/jbgui/GUserDefinedColours.java
src/jalview/project/Jalview2XML.java
src/jalview/rest/RestHandler.java
src/jalview/schemes/ColourSchemes.java
src/jalview/structure/StructureImportSettings.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/urls/IdOrgSettings.java
src/jalview/urls/IdentifiersUrlProvider.java
src/jalview/util/ColorUtils.java
src/jalview/util/DBRefUtils.java
src/jalview/util/MessageManager.java
src/jalview/util/Platform.java
src/jalview/util/ShortcutKeyMaskExWrapper.java
src/jalview/util/ShortcutKeyMaskExWrapperI.java
src/jalview/util/StringUtils.java
src/jalview/util/jarInputStreamProvider.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/OverviewDimensions.java
src/jalview/viewmodel/OverviewDimensionsHideHidden.java
src/jalview/viewmodel/OverviewDimensionsShowHidden.java
src/jalview/viewmodel/ViewportRanges.java
src/jalview/workers/AlignCalcManager.java
src/jalview/ws/DBRefFetcher.java
src/jalview/ws/SequenceFetcher.java
src/jalview/ws/SequenceFetcherFactory.java [deleted file]
src/jalview/ws/dbsources/EmblCdsSource.java
src/jalview/ws/dbsources/EmblSource.java
src/jalview/ws/dbsources/EmblXmlSource.java
src/jalview/ws/dbsources/Pdb.java
src/jalview/ws/dbsources/Uniprot.java
src/jalview/ws/jws1/Discoverer.java
src/jalview/ws/jws1/JPredClient.java
src/jalview/ws/jws1/MsaWSClient.java
src/jalview/ws/jws1/SeqSearchWSClient.java
src/jalview/ws/jws1/WS1Client.java
src/jalview/ws/jws2/JabaWsParamTest.java
src/jalview/ws/jws2/Jws2Discoverer.java
src/jalview/ws/jws2/MsaWSClient.java
src/jalview/ws/jws2/SeqAnnotationServiceCalcWorker.java
src/jalview/ws/jws2/SequenceAnnotationWSClient.java
src/jalview/ws/jws2/jabaws2/Jws2Instance.java
src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java
src/jalview/ws/rest/RestClient.java
src/jalview/ws/sifts/SiftsClient.java
src/jalview/ws/sifts/SiftsSettings.java
src/jalview/ws/utils/UrlDownloadClient.java
src/javajs/async/Assets.java [new file with mode: 0644]
src/javajs/async/Async.java [new file with mode: 0644]
src/javajs/async/AsyncColorChooser.java [new file with mode: 0644]
src/javajs/async/AsyncDialog.java [new file with mode: 0644]
src/javajs/async/AsyncFileChooser.java [new file with mode: 0644]
src/javajs/async/AsyncSwingWorker.java [new file with mode: 0644]
src/javajs/async/SwingJSUtils.java [new file with mode: 0644]
src/swingjs/api/Interface.java [new file with mode: 0644]
src/swingjs/api/JSFileHandler.java [new file with mode: 0644]
src/swingjs/api/JSUtilI.java
src/swingjs/api/js/DOMNode.java [new file with mode: 0644]
src/swingjs/api/js/HTML5AudioContext.java [new file with mode: 0644]
src/swingjs/api/js/HTML5Canvas.java [new file with mode: 0644]
src/swingjs/api/js/HTML5CanvasContext2D.java [new file with mode: 0644]
src/swingjs/api/js/HTML5DataTransfer.java [new file with mode: 0644]
src/swingjs/api/js/HTML5Video.java [new file with mode: 0644]
src/swingjs/api/js/J2SInterface.java [new file with mode: 0644]
src/swingjs/api/js/JQuery.java [new file with mode: 0644]
src/swingjs/api/js/JQueryObject.java [new file with mode: 0644]
src/swingjs/api/js/JSFunction.java [new file with mode: 0644]
src/swingjs/api/js/JSInterface.java [new file with mode: 0644]
src/swingjs/api/js/README.txt [new file with mode: 0644]
srcjar/fr/orsay/lri/varna/applications/fragseq/FragSeqTreeModel.java
swingjs/README-JALVIEW [new file with mode: 0644]
swingjs/ver/3.2.7/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.2.8/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.2.9-j11/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.2.9-j11/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar [new file with mode: 0644]
swingjs/ver/3.2.9-j11/timestamp [new file with mode: 0644]
swingjs/ver/3.2.9/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.2.9/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.9/net.sf.j2s.core.jar [new file with mode: 0644]
swingjs/ver/3.2.9/timestamp [new file with mode: 0644]
temp/bbb.dtd.pdf [new file with mode: 0644]
template.html [new file with mode: 0644]
test/jalview/analysis/AlignmentGenerator.java
test/jalview/analysis/AlignmentSorterTest.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/CrossRefTest.java
test/jalview/analysis/FinderTest.java
test/jalview/bin/CommandLineOperations.java
test/jalview/datamodel/SequenceTest.java
test/jalview/datamodel/features/FeatureAttributesTest.java
test/jalview/datamodel/features/SequenceFeaturesTest.java
test/jalview/ext/ensembl/EnsemblCdnaTest.java
test/jalview/ext/ensembl/EnsemblCdsTest.java
test/jalview/ext/ensembl/EnsemblGeneTest.java
test/jalview/ext/ensembl/EnsemblGenomeTest.java
test/jalview/ext/ensembl/EnsemblSeqProxyTest.java
test/jalview/ext/jmol/JmolCommandsTest.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/ext/jmol/JmolViewerTest.java
test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java
test/jalview/fts/service/pdb/PDBFTSPanelTest.java
test/jalview/gui/AlignFrameTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/gui/AlignmentPanelTest.java
test/jalview/gui/AnnotationChooserTest.java
test/jalview/gui/AnnotationColumnChooserTest.java
test/jalview/gui/AnnotationRowFilterTest.java
test/jalview/gui/CalculationChooserTest.java
test/jalview/gui/FreeUpMemoryTest.java
test/jalview/gui/JvSwingUtilsTest.java
test/jalview/gui/PairwiseAlignmentPanelTest.java
test/jalview/gui/PopupMenuTest.java
test/jalview/gui/SeqCanvasTest.java
test/jalview/gui/SeqPanelTest.java
test/jalview/gui/StructureChooserTest.java
test/jalview/hmmer/HMMERTest.java
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/BSMLFileTest.java [new file with mode: 0644]
test/jalview/io/BackupFilesTest.java
test/jalview/io/CrossRef2xmlTests.java
test/jalview/io/FeaturesFileTest.java
test/jalview/io/FileFormatsTest.java
test/jalview/io/Jalview2xmlBase.java
test/jalview/io/JalviewExportPropertiesTests.java
test/jalview/io/SequenceAnnotationReportTest.java
test/jalview/project/Jalview2xmlTests.java
test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java
test/jalview/renderer/seqfeatures/FeatureRendererTest.java
test/jalview/schemes/ColourSchemesTest.java
test/jalview/structure/Mapping.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/util/ColorUtilsTest.java
test/jalview/util/PlatformTest.java
test/jalview/ws/PDBSequenceFetcherTest.java
test/jalview/ws/dbsources/RemoteFormatTest.java
test/jalview/ws/jabaws/JalviewJabawsTestUtils.java
test/jalview/ws/sifts/SiftsClientTest.java
test/jalview/ws/utils/UrlDownloadClientTest.java
test/mc_view/PDBfileTest.java
unused/JalviewAppLoader.java [new file with mode: 0644]
unused/JalviewJSApp.java [new file with mode: 0644]
utils/clover/lib/clover-ant-4.4.1.jar [new file with mode: 0644]
utils/cmd-nox.sh [new file with mode: 0755]
utils/doc/github.css [moved from doc/github.css with 100% similarity]
utils/gradle-nox.sh [new symlink]
utils/jalviewjs/_j2sclasslist.txt [deleted file]
utils/jalviewjs/classlists/jalview.txt
utils/jalviewjs/classlists/jvexamplefile.txt [deleted file]
utils/jalviewjs/classlists/jvjmol.txt [new file with mode: 0644]
utils/jalviewjs/classlists/stevesoft.txt [new file with mode: 0644]
utils/jalviewjs/libjs/jmol-app.zip
utils/jalviewjs/site-resources/___j2sflags.htm [new file with mode: 0644]
utils/jalviewjs/site-resources/_jalview_bin_JalviewJS_core.html [moved from utils/jalviewjs/site-resources/jalview_bin_JalviewJS_core.html with 100% similarity]
utils/jalviewjs/site-resources/_jalview_embedded_example1.html [moved from utils/jalviewjs/site-resources/jalview_embedded_example1.html with 79% similarity]
utils/jalviewjs/site-resources/_jalview_embedded_example2.html [new file with mode: 0644]
utils/jalviewjs/template.html

index 389b981..e6980f2 100644 (file)
@@ -40,3 +40,6 @@ TESTNG
 /site1
 /site2
 /resources/.build_properties
+/build-site.xml
+/buildcore.xml
+/j11lib/intervalstore-v1.0.jar
index fa922d9..d99ec68 100644 (file)
@@ -28,7 +28,7 @@ htsjdk-2.12.0.jar     built from maven master at https://github.com/samtools/htsjdk
 httpclient-4.0.3.jar
 httpcore-4.0.1.jar
 httpmime-4.0.3.jar
-intervalstore-v1.0.jar
+intervalstore-v1.1.jar
 jabaws-min-client-2.2.0.jar
 java-json.jar
 jaxrpc.jar
diff --git a/build-libjs.xml b/build-libjs.xml
new file mode 100644 (file)
index 0000000..3fb3cd3
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!--
+ * just a crude zip up of non-Jalview classes for development purposes -BH 2018
+ *
+ * external JAR class treatment for JavaScript: see src2/README_SWINGJS.txt
+ * 
+ -->
+
+<project name="jalviewX" default="zipall" basedir="."
+ xmlns:if="ant:if"
+    xmlns:unless="ant:unless">
+
+       <!-- inputs directories -->
+    <property name="resource.dir" value="resources" />         
+    <property name="swingjs.dir" value="swingjs"/>
+       <!-- output directories -->
+       <property name="site.dir" value="site"/>
+       <property name="j2s.dir" value="${site.dir}/swingjs/j2s"/>
+       <property name="libjs.dir" value="libjs"/>
+
+       <target name="zipall" depends="zipvarna,zipmig,zipintervalstore">
+               
+               
+  </target>
+
+  <target name="zipvarna">
+    <!-- VARNA -->
+           <property name="varna.zip" value="${libjs.dir}/VARNA-site.zip" />                   
+               <echo> Zipping up ${varna.zip} </echo>
+               <zip destfile="${varna.zip}" basedir="${site.dir}" includes="fr_*.html,swingjs/j2s/fr/**" />
+       </target>
+
+       <target name="zipmig">
+         <!-- net.miginfo.com MiGLayout -->
+                   <property name="mig.zip" value="${libjs.dir}/MiGLayout-site.zip" />                 
+                       <echo> Zipping up ${mig.zip} </echo>
+                       <zip destfile="${mig.zip}" basedir="${site.dir}" includes="swingjs/j2s/net/miginfocom/**" />
+       </target>
+
+       <target name="zipintervalstore">
+         <!-- intervalstore.impl NCList implementation -->
+                   <property name="intervalstore.zip" value="${libjs.dir}/intervalstore-site.zip" />                   
+                       <echo> Zipping up ${intervalstore.zip} </echo>
+                       <zip destfile="${intervalstore.zip}" basedir="${site.dir}" includes="swingjs/j2s/intervalstore/**" />
+       </target>
+
+       <!-- already in SwingJS
+       <target name="zipjson"  already in SwingJS>
+                   <property name="json.zip" value="${libjs.dir}/json-site.zip" />                     
+                       <echo> Zipping up ${json.zip} </echo>
+                       <zip destfile="${json.zip}" basedir="${site.dir}" includes="swingjs/j2s/org/json/**" />
+       </target>
+       -->
+
+       <!-- log4j minimal implementation is already in jalview/javascript
+             and is mapped from org.apache.log4j by the following .j2s line:
+             
+             j2s.class.replacements=org.apache.log4j.->jalview.javascript.log4j.
+              
+       <target name="ziplog4j">
+                 <!- org.apache.log4j ->
+                   <property name="log4j.zip" value="${libjs.dir}/log4j-site.zip" />                   
+                       <echo> Zipping up ${log4j.zip} </echo>
+                       <zip destfile="${log4j.zip}" basedir="${site.dir}" includes="swingjs/j2s/org/apache/log4j/**" />
+       </target>
+    -->
+</project>
index 985cc87..147c31a 100644 (file)
@@ -1,3 +1,6 @@
+/* Convention for properties.  Read from gradle.properties, use lower_case_underlines for property names.
+ * For properties set within build.gradle, use camelCaseNoSpace.
+ */
 import org.apache.tools.ant.filters.ReplaceTokens
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject
@@ -9,7 +12,16 @@ import java.security.MessageDigest
 import groovy.transform.ExternalizeMethods
 import groovy.util.XmlParser
 import groovy.xml.XmlUtil
-
+import com.vladsch.flexmark.util.ast.Node
+import com.vladsch.flexmark.html.HtmlRenderer
+import com.vladsch.flexmark.parser.Parser
+import com.vladsch.flexmark.util.data.MutableDataSet
+import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
+import com.vladsch.flexmark.ext.tables.TablesExtension
+import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
+import com.vladsch.flexmark.ext.autolink.AutolinkExtension
+import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
+import com.vladsch.flexmark.ext.toc.TocExtension
 
 buildscript {
   repositories {
@@ -17,7 +29,7 @@ buildscript {
     mavenLocal()
   }
   dependencies {
-    classpath 'org.openclover:clover:4.4.1'
+    classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
   }
 }
 
@@ -82,6 +94,26 @@ ext {
     }
   }
 
+  ////  
+  // Import releaseProps from the RELEASE file
+  // or a file specified via JALVIEW_RELEASE_FILE if defined
+  // Expect jalview.version and target release branch in jalview.release        
+  def releaseProps = new Properties();
+  def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
+  def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
+  try {
+    (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream { 
+     releaseProps.load(it)
+    }
+  } catch (Exception fileLoadError) {
+    throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
+  }
+  ////
+  // Set JALVIEW_VERSION if it is not already set
+  if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
+    JALVIEW_VERSION = releaseProps.get("jalview.version")
+  }
+  
   // this property set when running Eclipse headlessly
   j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
   // this property set by Eclipse
@@ -103,8 +135,7 @@ ext {
   J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
   if (J2S_ENABLED) {
     println("J2S ENABLED")
-  }
-  
+  } 
   /* *-/
   System.properties.sort { it.key }.each {
     key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
@@ -120,21 +151,25 @@ ext {
   sourceDir = string("${jalviewDir}/${bareSourceDir}")
   resourceDir = string("${jalviewDir}/${resource_dir}")
   bareTestSourceDir = string(test_source_dir)
-  testSourceDir = string("${jalviewDir}/${bareTestSourceDir}")
+  testDir = string("${jalviewDir}/${bareTestSourceDir}")
 
-  // clover
-  cloverInstrDir = file("${buildDir}/${cloverSourcesInstrDir}")
-  cloverDb = string("${buildDir}/clover/clover.db")
   classesDir = string("${jalviewDir}/${classes_dir}")
-  if (clover.equals("true")) {
-    use_clover = true
-    classesDir = string("${buildDir}/${cloverClassesDir}")
-  } else {
-    use_clover = false
-    classesDir = string("${jalviewDir}/${classes_dir}")
-  }
 
-  classes = classesDir
+  // clover
+  useClover = clover.equals("true")
+  cloverBuildDir = "${buildDir}/clover"
+  cloverInstrDir = file("${cloverBuildDir}/clover-instr")
+  cloverClassesDir = file("${cloverBuildDir}/clover-classes")
+  cloverReportDir = file("${buildDir}/reports/clover")
+  cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
+  cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
+  //cloverTestClassesDir = cloverClassesDir
+  cloverDb = string("${cloverBuildDir}/clover.db")
+
+  resourceClassesDir = useClover ? cloverClassesDir : classesDir
+
+  testSourceDir = useClover ? cloverTestInstrDir : testDir
+  testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
 
   getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
   buildDist = true
@@ -163,8 +198,8 @@ ext {
     getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
     jvlChannelName += "_${getdownChannelName}"
     // automatically add the test group Not-bamboo for exclusion 
-    if ("".equals(testngExcludedGroups)) { 
-      testngExcludedGroups = "Not-bamboo"
+    if ("".equals(testng_excluded_groups)) { 
+      testng_excluded_groups = "Not-bamboo"
     }
     install4jExtraScheme = "jalviewb"
     break
@@ -172,11 +207,6 @@ ext {
     case "RELEASE":
     getdownAppDistDir = getdown_app_dir_release
     reportRsyncCommand = true
-    // Don't ignore transpile errors for release build
-    if (jalviewjs_ignore_transpile_errors.equals("true")) {
-      jalviewjs_ignore_transpile_errors = "false"
-      println("Setting jalviewjs_ignore_transpile_errors to 'false'")
-    }
     install4jSuffix = ""
     install4jDSStore = "DS_Store"
     install4jDMGBackgroundImage = "jalview_dmg_background.png"
@@ -187,11 +217,11 @@ ext {
     getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
-    if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
+    if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
     } else {
-      packageDir = string("${ARCHIVEDIR}/${packageDir}")
-      buildProperties = string("${buildDir}/archive/${build_properties_file}")
+      package_dir = string("${ARCHIVEDIR}/${package_dir}")
+      buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
       buildDist = false
     }
     reportRsyncCommand = true
@@ -202,11 +232,11 @@ ext {
     getdownChannelName = string("archive/${JALVIEW_VERSION}")
     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
     getdownAppBase = file(getdownWebsiteDir).toURI().toString()
-    if (!file("${ARCHIVEDIR}/${packageDir}").exists()) {
+    if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
     } else {
-      packageDir = string("${ARCHIVEDIR}/${packageDir}")
-      buildProperties = string("${buildDir}/archive/${build_properties_file}")
+      package_dir = string("${ARCHIVEDIR}/${package_dir}")
+      buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
       buildDist = false
     }
     reportRsyncCommand = true
@@ -217,6 +247,11 @@ ext {
 
     case "DEVELOP":
     reportRsyncCommand = true
+    
+    // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
+    JALVIEW_VERSION=JALVIEW_VERSION+"-develop"
+    
+    install4jSuffix = "Develop"
     install4jDSStore = "DS_Store-DEVELOP"
     install4jDMGBackgroundImage = "jalview_dmg_background-DEVELOP.png"
     install4jExtraScheme = "jalviewd"
@@ -230,7 +265,7 @@ ext {
       jalviewjs_ignore_transpile_errors = "false"
       println("Setting jalviewjs_ignore_transpile_errors to 'false'")
     }
-    JALVIEW_VERSION = "TEST"
+    JALVIEW_VERSION = JALVIEW_VERSION+"-test"
     install4jSuffix = "Test"
     install4jDSStore = "DS_Store-TEST-RELEASE"
     install4jDMGBackgroundImage = "jalview_dmg_background-TEST.png"
@@ -240,6 +275,8 @@ ext {
 
     case ~/^SCRATCH(|-[-\w]*)$/:
     getdownChannelName = CHANNEL
+    JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
+    
     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
     getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
     reportRsyncCommand = true
@@ -262,6 +299,7 @@ ext {
     break
 
     case "LOCAL":
+    JALVIEW_VERSION = "TEST"
     getdownAppBase = file(getdownWebsiteDir).toURI().toString()
     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
     install4jExtraScheme = "jalviewl"
@@ -401,8 +439,8 @@ ext {
 
 
 
-  buildingHTML = string("${jalviewDir}/${docDir}/building.html")
-  helpFile = string("${classesDir}/${help_dir}/help.jhm")
+  buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
+  helpFile = string("${resourceClassesDir}/${help_dir}/help.jhm")
   helpParentDir = string("${jalviewDir}/${help_parent_dir}")
   helpSourceDir = string("${helpParentDir}/${help_dir}")
 
@@ -419,7 +457,6 @@ ext {
   jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
   jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
   jalviewjsJalviewCoreHtmlFile = string("")
-  jalviewjsJalviewCoreName = string(jalviewjs_core_name)
   jalviewjsCoreClasslists = []
   jalviewjsJalviewTemplateName = string(jalviewjs_name)
   jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
@@ -446,10 +483,9 @@ sourceSets {
       srcDirs += helpParentDir
     }
 
-    jar.destinationDir = file("${jalviewDir}/${packageDir}")
+    jar.destinationDir = file("${jalviewDir}/${package_dir}")
 
     compileClasspath = files(sourceSets.main.java.outputDir)
-    //compileClasspath += files(sourceSets.main.resources.srcDirs)
     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
 
     runtimeClasspath = compileClasspath
@@ -457,18 +493,19 @@ sourceSets {
 
   clover {
     java {
-      srcDirs = [ cloverInstrDir ]
-      outputDir = file("${buildDir}/${cloverClassesDir}")
+      srcDirs cloverInstrDir
+      outputDir = cloverClassesDir
     }
 
     resources {
       srcDirs = sourceSets.main.resources.srcDirs
     }
-    compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
-    compileClasspath += files(sourceSets.main.java.outputDir)
-    compileClasspath += sourceSets.main.compileClasspath
-    compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["**/*.jar"])
+
+    compileClasspath = files( sourceSets.clover.java.outputDir )
+    //compileClasspath += files( testClassesDir )
     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
+    compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
+    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
 
     runtimeClasspath = compileClasspath
   }
@@ -476,42 +513,20 @@ sourceSets {
   test {
     java {
       srcDirs testSourceDir
-      outputDir = file("${jalviewDir}/${testOutputDir}")
+      outputDir = file(testClassesDir)
     }
 
     resources {
-      srcDirs = sourceSets.main.resources.srcDirs
+      srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
     }
 
     compileClasspath = files( sourceSets.test.java.outputDir )
-
-    if (use_clover) {
-      compileClasspath = sourceSets.clover.compileClasspath
-    } else {
-      compileClasspath += files(sourceSets.main.java.outputDir)
-    }
-
-    compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
-    compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testnglibs", include: ["**/*.jar"])
-    compileClasspath += fileTree(dir: "${jalviewDir}/${utilsDir}/testlibs", include: ["**/*.jar"])
+    compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
+    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
 
     runtimeClasspath = compileClasspath
   }
-}
-
-
-// clover bits
-dependencies {
-  if (use_clover) {
-    cloverCompile 'org.openclover:clover:4.4.1'
-    testCompile 'org.openclover:clover:4.4.1'
-  }
-}
-
 
-configurations {
-  cloverRuntime
-  cloverRuntime.extendsFrom cloverCompile
 }
 
 
@@ -625,7 +640,6 @@ eclipse {
     javaRuntimeName = eclipseJavaRuntimeName
 
     // add in jalview project specific properties/preferences into eclipse core preferences
-    // and also the codestyle XML file
     file {
       withProperties { props ->
         def jalview_prefs = new Properties()
@@ -724,61 +738,209 @@ eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
 /* end of eclipse preferences hack */
 
 
-task cloverInstr {
-  // only instrument source, we build test classes as normal
-  inputs.files files (sourceSets.main.allJava,sourceSets.test.allJava) // , fileTree(dir:"$jalviewDir/$testSourceDir", include: ["**/*.java"]))
-  outputs.dir cloverInstrDir
+// clover bits
+
 
+task cleanClover {
   doFirst {
-    delete cloverInstrDir
-    def argsList = [
-      "--initstring",
-      cloverDb,
-      "-d",
-      cloverInstrDir.getPath(),
-    ]
-    argsList.addAll(
-      inputs.files.files.collect(
-        { file -> file.absolutePath }
-      )
+    delete cloverBuildDir
+    delete cloverReportDir
+  }
+}
+
+
+task cloverInstrJava(type: JavaExec) {
+  group = "Verification"
+  description = "Create clover instrumented source java files"
+
+  dependsOn cleanClover
+
+  inputs.files(sourceSets.main.allJava)
+  outputs.dir(cloverInstrDir)
+
+  //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
+  classpath = sourceSets.clover.compileClasspath
+  main = "com.atlassian.clover.CloverInstr"
+
+  def argsList = [
+    "--encoding",
+    "UTF-8",
+    "--initstring",
+    cloverDb,
+    "--destdir",
+    cloverInstrDir.getPath(),
+  ]
+  def srcFiles = sourceSets.main.allJava.files
+  argsList.addAll(
+    srcFiles.collect(
+      { file -> file.absolutePath }
     )
-    String[] args = argsList.toArray()
-    println("About to instrument "+args.length +" files")
-    com.atlassian.clover.CloverInstr.mainImpl(args)
+  )
+  args argsList.toArray()
+
+  doFirst {
+    delete cloverInstrDir
+    println("Clover: About to instrument "+srcFiles.size() +" files")
   }
 }
 
 
+task cloverInstrTests(type: JavaExec) {
+  group = "Verification"
+  description = "Create clover instrumented source test files"
+
+  dependsOn cleanClover
+
+  inputs.files(testDir)
+  outputs.dir(cloverTestInstrDir)
+
+  classpath = sourceSets.clover.compileClasspath
+  main = "com.atlassian.clover.CloverInstr"
+
+  def argsList = [
+    "--encoding",
+    "UTF-8",
+    "--initstring",
+    cloverDb,
+    "--srcdir",
+    testDir,
+    "--destdir",
+    cloverTestInstrDir.getPath(),
+  ]
+  args argsList.toArray()
+
+  doFirst {
+    delete cloverTestInstrDir
+    println("Clover: About to instrument test files")
+  }
+}
+
+
+task cloverInstr {
+  group = "Verification"
+  description = "Create clover instrumented all source files"
+
+  dependsOn cloverInstrJava
+  dependsOn cloverInstrTests
+}
+
+
 cloverClasses.dependsOn cloverInstr
 
 
-task cloverReport {
+task cloverConsoleReport(type: JavaExec) {
   group = "Verification"
-  description = "Creates the Clover report"
-  inputs.dir "${buildDir}/clover"
-  outputs.dir "${reportsDir}/clover"
+  description = "Creates clover console report"
+
   onlyIf {
     file(cloverDb).exists()
   }
-  doFirst {
-    def argsList = [
-      "--initstring",
-      cloverDb,
-      "-o",
-      "${reportsDir}/clover"
-    ]
-    String[] args = argsList.toArray()
-    com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
 
-    // and generate ${reportsDir}/clover/clover.xml
-    args = [
-      "--initstring",
-      cloverDb,
-      "-o",
-      "${reportsDir}/clover/clover.xml"
-    ].toArray()
-    com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
+  inputs.dir cloverClassesDir
+
+  classpath = sourceSets.clover.runtimeClasspath
+  main = "com.atlassian.clover.reporters.console.ConsoleReporter"
+
+  if (cloverreport_mem.length() > 0) {
+    maxHeapSize = cloverreport_mem
+  }
+  if (cloverreport_jvmargs.length() > 0) {
+    jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
   }
+
+  def argsList = [
+    "--alwaysreport",
+    "--initstring",
+    cloverDb,
+    "--unittests"
+  ]
+
+  args argsList.toArray()
+}
+
+
+task cloverHtmlReport(type: JavaExec) {
+  group = "Verification"
+  description = "Creates clover HTML report"
+
+  onlyIf {
+    file(cloverDb).exists()
+  }
+
+  def cloverHtmlDir = cloverReportDir
+  inputs.dir cloverClassesDir
+  outputs.dir cloverHtmlDir
+
+  classpath = sourceSets.clover.runtimeClasspath
+  main = "com.atlassian.clover.reporters.html.HtmlReporter"
+
+  if (cloverreport_mem.length() > 0) {
+    maxHeapSize = cloverreport_mem
+  }
+  if (cloverreport_jvmargs.length() > 0) {
+    jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
+  }
+
+  def argsList = [
+    "--alwaysreport",
+    "--initstring",
+    cloverDb,
+    "--outputdir",
+    cloverHtmlDir
+  ]
+
+  if (cloverreport_html_options.length() > 0) {
+    argsList += cloverreport_html_options.split(" ")
+  }
+
+  args argsList.toArray()
+}
+
+
+task cloverXmlReport(type: JavaExec) {
+  group = "Verification"
+  description = "Creates clover XML report"
+
+  onlyIf {
+    file(cloverDb).exists()
+  }
+
+  def cloverXmlFile = "${cloverReportDir}/clover.xml"
+  inputs.dir cloverClassesDir
+  outputs.file cloverXmlFile
+
+  classpath = sourceSets.clover.runtimeClasspath
+  main = "com.atlassian.clover.reporters.xml.XMLReporter"
+
+  if (cloverreport_mem.length() > 0) {
+    maxHeapSize = cloverreport_mem
+  }
+  if (cloverreport_jvmargs.length() > 0) {
+    jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
+  }
+
+  def argsList = [
+    "--alwaysreport",
+    "--initstring",
+    cloverDb,
+    "--outfile",
+    cloverXmlFile
+  ]
+
+  if (cloverreport_xml_options.length() > 0) {
+    argsList += cloverreport_xml_options.split(" ")
+  }
+
+  args argsList.toArray()
+}
+
+
+task cloverReport {
+  group = "Verification"
+  description = "Creates clover reports"
+
+  dependsOn cloverXmlReport
+  dependsOn cloverHtmlReport
 }
 
 
@@ -790,43 +952,29 @@ compileCloverJava {
     options.compilerArgs += additional_compiler_args
     print ("Setting target compatibility to "+targetCompatibility+"\n")
   }
-  classpath += configurations.cloverRuntime
-}
-
-
-task cleanClover {
-  doFirst {
-    delete cloverInstrDir
-    delete cloverDb
-  }
+  //classpath += configurations.cloverRuntime
 }
 // end clover bits
 
 
 compileJava {
-       
+  // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
   sourceCompatibility = compile_source_compatibility
   targetCompatibility = compile_target_compatibility
   options.compilerArgs = additional_compiler_args
   options.encoding = "UTF-8"
   doFirst {
-    print ("Setting target compatibility to "+targetCompatibility+"\n")
+    print ("Setting target compatibility to "+compile_target_compatibility+"\n")
   }
 
 }
 
 
 compileTestJava {
-  if (use_clover) {
-    dependsOn compileCloverJava
-    classpath += configurations.cloverRuntime
-  } else {
-    classpath += sourceSets.main.runtimeClasspath
-  }
+  sourceCompatibility = compile_source_compatibility
+  targetCompatibility = compile_target_compatibility
+  options.compilerArgs = additional_compiler_args
   doFirst {
-    sourceCompatibility = compile_source_compatibility
-    targetCompatibility = compile_target_compatibility
-    options.compilerArgs = additional_compiler_args
     print ("Setting target compatibility to "+targetCompatibility+"\n")
   }
 }
@@ -855,22 +1003,25 @@ def getDate(format) {
 
 
 task setGitVals {
-  def hashStdOut = new ByteArrayOutputStream()
-  exec {
-    commandLine "git", "rev-parse", "--short", "HEAD"
-    standardOutput = hashStdOut
-    ignoreExitValue true
-  }
 
-  def branchStdOut = new ByteArrayOutputStream()
-  exec {
-    commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
-    standardOutput = branchStdOut
-    ignoreExitValue true
-  }
+  doFirst {
+    def hashStdOut = new ByteArrayOutputStream()
+    def resultHash = exec {
+      commandLine "git", "rev-parse", "--short", "HEAD"
+      standardOutput = hashStdOut
+      ignoreExitValue true
+    }
 
-  gitHash = hashStdOut.toString().trim()
-  gitBranch = branchStdOut.toString().trim()
+    def branchStdOut = new ByteArrayOutputStream()
+    def resultBranch = exec {
+      commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
+      standardOutput = branchStdOut
+      ignoreExitValue true
+    }
+
+    gitHash = resultHash.getExitValue() == 0 ? hashStdOut.toString().trim() : "NO_GIT_COMMITID_FOUND"
+    gitBranch = resultBranch.getExitValue() == 0 ? branchStdOut.toString().trim() : "NO_GIT_BRANCH_FOUND"
+  }
 
   outputs.upToDateWhen { false }
 }
@@ -909,53 +1060,116 @@ task cleanBuildingHTML(type: Delete) {
 }
 
 
-task convertBuildingMD(type: Exec) {
+def convertMdToHtml (FileTree mdFiles, File cssFile) {
+  MutableDataSet options = new MutableDataSet()
+
+  def extensions = new ArrayList<>()
+  extensions.add(AnchorLinkExtension.create()) 
+  extensions.add(AutolinkExtension.create())
+  extensions.add(StrikethroughExtension.create())
+  extensions.add(TaskListExtension.create())
+  extensions.add(TablesExtension.create())
+  extensions.add(TocExtension.create())
+  
+  options.set(Parser.EXTENSIONS, extensions)
+
+  // set GFM table parsing options
+  options.set(TablesExtension.WITH_CAPTION, false)
+  options.set(TablesExtension.COLUMN_SPANS, false)
+  options.set(TablesExtension.MIN_HEADER_ROWS, 1)
+  options.set(TablesExtension.MAX_HEADER_ROWS, 1)
+  options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
+  options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
+  options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
+  // GFM anchor links
+  options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
+  options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
+  options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
+  options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
+
+  Parser parser = Parser.builder(options).build()
+  HtmlRenderer renderer = HtmlRenderer.builder(options).build()
+
+  mdFiles.each { mdFile ->
+    // add table of contents
+    def mdText = "[TOC]\n"+mdFile.text
+
+    // grab the first top-level title
+    def title = null
+    def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
+    def matcher = mdText =~ titleRegex
+    if (matcher.size() > 0) {
+      // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
+      title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
+    }
+    // or use the filename if none found
+    if (title == null) {
+      title = mdFile.getName()
+    }
+
+    Node document = parser.parse(mdText)
+    String htmlBody = renderer.render(document)
+    def htmlText = '''<html>
+<!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>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta http-equiv="Content-Style-Type" content="text/css" />
+    <meta name="generator" content="flexmark" />
+'''
+    htmlText += ((title != null) ? "  <title>${title}</title>" : '' )
+    htmlText += '''
+    <style type="text/css">code{white-space: pre;}</style>
+'''
+    htmlText += ((cssFile != null) ? cssFile.text : '')
+    htmlText += '''</head>
+  <body>
+'''
+    htmlText += htmlBody
+    htmlText += '''
+  </body>
+</html>
+'''
+
+    def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
+    def htmlFile = file(htmlFilePath)
+    htmlFile.text = htmlText
+  }
+}
+
+
+task convertMdFiles {
   dependsOn cleanBuildingHTML
-  def buildingMD = "${jalviewDir}/${docDir}/building.md"
-  def css = "${jalviewDir}/${docDir}/github.css"
+  def mdFiles = fileTree(dir: "${jalviewDir}/${doc_dir}", include: "*.md")
+  def cssFile = file("${jalviewDir}/${flexmark_css}")
 
-  def pandoc = null
-  pandoc_exec.split(",").each {
-    if (file(it.trim()).exists()) {
-      pandoc = it.trim()
-      return true
-    }
+  doLast {
+    convertMdToHtml(mdFiles, cssFile)
   }
 
-  def buildtoolsPandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
-  if ((pandoc == null || ! file(pandoc).exists()) && file(buildtoolsPandoc).exists()) {
-    pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
-  }
+  inputs.files(mdFiles)
+  inputs.file(cssFile)
 
-  doFirst {
-    if (pandoc != null && file(pandoc).exists()) {
-        commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
-    } else {
-        println("Cannot find pandoc. Skipping convert building.md to HTML")
-        throw new StopExecutionException("Cannot find pandoc. Skipping convert building.md to HTML")
-    }
+  def htmlFiles = []
+  mdFiles.each { mdFile ->
+    def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
+    htmlFiles.add(file(htmlFilePath))
   }
-
-  ignoreExitValue true
-
-  inputs.file(buildingMD)
-  inputs.file(css)
-  outputs.file(buildingHTML)
+  outputs.files(htmlFiles)
 }
 
 
 task syncDocs(type: Sync) {
-  dependsOn convertBuildingMD
-  def syncDir = "${classesDir}/${docDir}"
-  from fileTree("${jalviewDir}/${docDir}")
+  dependsOn convertMdFiles
+  def syncDir = "${classesDir}/${doc_dir}"
+  from fileTree("${jalviewDir}/${doc_dir}")
   into syncDir
-
 }
 
 
 task copyHelp(type: Copy) {
   def inputDir = helpSourceDir
-  def outputDir = "${classesDir}/${help_dir}"
+  def outputDir = "${resourceClassesDir}/${help_dir}"
   from(inputDir) {
     exclude '**/*.gif'
     exclude '**/*.jpg'
@@ -983,7 +1197,7 @@ task copyHelp(type: Copy) {
 
 
 task syncLib(type: Sync) {
-  def syncDir = "${classesDir}/${libDistDir}"
+  def syncDir = "${resourceClassesDir}/${libDistDir}"
   from fileTree("${jalviewDir}/${libDistDir}")
   into syncDir
 }
@@ -993,7 +1207,7 @@ task syncResources(type: Sync) {
   dependsOn createBuildProperties
   from resourceDir
   include "**/*.*"
-  into "${classesDir}"
+  into "${resourceClassesDir}"
   preserve {
     include "**"
   }
@@ -1010,18 +1224,17 @@ task prepare {
 //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
 test {
   dependsOn prepare
-  dependsOn compileJava
-  if (use_clover) {
-    dependsOn cloverInstr
-  }
+  //dependsOn compileJava ////? DELETE
 
-  if (use_clover) {
-    print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n")
+  if (useClover) {
+    dependsOn cloverClasses
+   } else { //?
+     dependsOn compileJava //?
   }
 
   useTestNG() {
-    includeGroups testngGroups
-    excludeGroups testngExcludedGroups
+    includeGroups testng_groups
+    excludeGroups testng_excluded_groups
     preserveOrder true
     useDefaultListeners=true
   }
@@ -1034,6 +1247,11 @@ test {
   targetCompatibility = compile_target_compatibility
   jvmArgs += additional_compiler_args
 
+  doFirst {
+    if (useClover) {
+      println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
+    }
+  }
 }
 
 
@@ -1059,22 +1277,22 @@ task buildIndices(type: JavaExec) {
 
 task compileLinkCheck(type: JavaCompile) {
   options.fork = true
-  classpath = files("${jalviewDir}/${utilsDir}")
-  destinationDir = file("${jalviewDir}/${utilsDir}")
-  source = fileTree(dir: "${jalviewDir}/${utilsDir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
+  classpath = files("${jalviewDir}/${utils_dir}")
+  destinationDir = file("${jalviewDir}/${utils_dir}")
+  source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
 
-  inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
-  inputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.java")
-  outputs.file("${jalviewDir}/${utilsDir}/HelpLinksChecker.class")
-  outputs.file("${jalviewDir}/${utilsDir}/BufferedLineReader.class")
+  inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
+  inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
+  outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
+  outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
 }
 
 
 task linkCheck(type: JavaExec) {
   dependsOn prepare, compileLinkCheck
 
-  def helpLinksCheckerOutFile = file("${jalviewDir}/${utilsDir}/HelpLinksChecker.out")
-  classpath = files("${jalviewDir}/${utilsDir}")
+  def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
+  classpath = files("${jalviewDir}/${utils_dir}")
   main = "HelpLinksChecker"
   workingDir = jalviewDir
   args = [ "${classesDir}/${help_dir}", "-nointernet" ]
@@ -1095,12 +1313,12 @@ task linkCheck(type: JavaExec) {
 // import the pubhtmlhelp target
 ant.properties.basedir = "${jalviewDir}"
 ant.properties.helpBuildDir = "${jalviewDirAbsolutePath}/${classes_dir}/${help_dir}"
-ant.importBuild "${utilsDir}/publishHelp.xml"
+ant.importBuild "${utils_dir}/publishHelp.xml"
 
 
 task cleanPackageDir(type: Delete) {
   doFirst {
-    delete fileTree(dir: "${jalviewDir}/${packageDir}", include: "*.jar")
+    delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
   }
 }
 
@@ -1111,13 +1329,13 @@ jar {
   dependsOn createBuildProperties
 
   manifest {
-    attributes "Main-Class": mainClass,
+    attributes "Main-Class": main_class,
     "Permissions": "all-permissions",
     "Application-Name": "Jalview Desktop",
     "Codebase": application_codebase
   }
 
-  destinationDir = file("${jalviewDir}/${packageDir}")
+  destinationDir = file("${jalviewDir}/${package_dir}")
   archiveName = rootProject.name+".jar"
 
   exclude "cache*/**"
@@ -1127,20 +1345,20 @@ jar {
   exclude "**/*.jar.*"
 
   inputs.dir(classesDir)
-  outputs.file("${jalviewDir}/${packageDir}/${archiveName}")
+  outputs.file("${jalviewDir}/${package_dir}/${archiveName}")
 }
 
 
 task copyJars(type: Copy) {
   from fileTree(dir: classesDir, include: "**/*.jar").files
-  into "${jalviewDir}/${packageDir}"
+  into "${jalviewDir}/${package_dir}"
 }
 
 
 // doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
 task syncJars(type: Sync) {
   from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
-  into "${jalviewDir}/${packageDir}"
+  into "${jalviewDir}/${package_dir}"
   preserve {
     include jar.archiveName
   }
@@ -1156,7 +1374,7 @@ task makeDist {
   dependsOn cleanPackageDir
   dependsOn syncJars
   dependsOn jar
-  outputs.dir("${jalviewDir}/${packageDir}")
+  outputs.dir("${jalviewDir}/${package_dir}")
 }
 
 
@@ -1168,6 +1386,7 @@ task cleanDist {
 
 shadowJar {
   group = "distribution"
+  description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
   if (buildDist) {
     dependsOn makeDist
   }
@@ -1177,7 +1396,7 @@ shadowJar {
   manifest {
     attributes 'Implementation-Version': JALVIEW_VERSION
   }
-  mainClassName = shadowJarMainClass
+  mainClassName = shadow_jar_main_class
   mergeServiceFiles()
   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
   minimize()
@@ -1269,7 +1488,7 @@ task getdownWebsite() {
     }
 
     def codeFiles = []
-    fileTree(file(packageDir)).each{ f ->
+    fileTree(file(package_dir)).each{ f ->
       if (f.isDirectory()) {
         def files = fileTree(dir: f, include: ["*"]).getFiles()
         codeFiles += files
@@ -1306,7 +1525,7 @@ task getdownWebsite() {
     // getdown-launcher.jar should not be in main application class path so the main application can move it when updated.  Listed as a resource so it gets updated.
     //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
     getdownTextString += "resource = ${getdown_launcher_new}\n"
-    getdownTextString += "class = ${mainClass}\n"
+    getdownTextString += "class = ${main_class}\n"
 
     def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
     getdown_txt.write(getdownTextString)
@@ -1364,7 +1583,7 @@ task getdownWebsite() {
   }
 
   if (buildDist) {
-    inputs.dir("${jalviewDir}/${packageDir}")
+    inputs.dir("${jalviewDir}/${package_dir}")
   }
   outputs.dir(getdownWebsiteDir)
   outputs.dir(getdownFilesDir)
@@ -1373,8 +1592,10 @@ task getdownWebsite() {
 
 // a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
 task getdownDigestDir(type: JavaExec) {
+  group "Help"
+  description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
+
   def digestDirPropertyName = "DIGESTDIR"
-  description = "Digest a local dir (-P${digestDirPropertyName}=...) for getdown"
   doFirst {
     classpath = files(getdownLauncher)
     def digestDir = findProperty(digestDirPropertyName)
@@ -1499,7 +1720,8 @@ task copyInstall4jTemplate {
     // NB we're deleting the /other/ one!
     // Also remove the examples subdir from non-release versions
     def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
-    if (CHANNEL=="RELEASE") {
+    // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
+    if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
       customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
     } else {
       // remove the examples subdir from Full File Set
@@ -1639,7 +1861,11 @@ spotless {
 
 
 task sourceDist(type: Tar) {
+  group "distribution"
+  description "Create a source .tar.gz file for distribution"
   
+  dependsOn convertMdFiles
+
   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
   def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
   // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
@@ -2296,10 +2522,6 @@ task jalviewjsBuildAllCores {
     ]
   }
 
-  // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
-  //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
-  classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
-
   jalviewjsCoreClasslists = []
 
   classlistFiles.each {
@@ -2322,8 +2544,8 @@ task jalviewjsBuildAllCores {
     }
     def list = fileTree(dir: j2sDir, includes: filelist)
 
-    def jsfile = "${outputDir}/core${name}.js"
-    def zjsfile = "${outputDir}/core${name}.z.js"
+    def jsfile = "${outputDir}/core_${name}.js"
+    def zjsfile = "${outputDir}/core_${name}.z.js"
 
     jalviewjsCoreClasslists += [
       'jsfile': jsfile,
@@ -2338,21 +2560,8 @@ task jalviewjsBuildAllCores {
     outputs.file(zjsfile)
   }
   
-  // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
-  def stevesoftClasslistName = "_stevesoft"
-  def stevesoftClasslist = [
-    'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
-    'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
-    'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
-    'name': stevesoftClasslistName
-  ]
-  jalviewjsCoreClasslists += stevesoftClasslist
-  inputs.files(stevesoftClasslist['list'])
-  outputs.file(stevesoftClasslist['jsfile'])
-  outputs.file(stevesoftClasslist['zjsfile'])
-
   // _all core
-  def allClasslistName = "_all"
+  def allClasslistName = "all"
   def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
   allJsFiles += fileTree(
     dir: libJ2sDir,
@@ -2375,8 +2584,8 @@ task jalviewjsBuildAllCores {
     ]
   )
   def allClasslist = [
-    'jsfile': "${outputDir}/core${allClasslistName}.js",
-    'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
+    'jsfile': "${outputDir}/core_${allClasslistName}.js",
+    'zjsfile': "${outputDir}/core_${allClasslistName}.z.js",
     'list': allJsFiles,
     'name': allClasslistName
   ]
@@ -2414,7 +2623,7 @@ def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inpu
       beginToken: '_',
       endToken: '_',
       tokens: [
-        'MAIN': '"'+mainClass+'"',
+        'MAIN': '"'+main_class+'"',
         'CODE': "null",
         'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
         'COREKEY': jalviewjs_core_key,
@@ -2634,10 +2843,25 @@ task jalviewjsIDE_checkJ2sPlugin {
     if (eclipseHome == null || ! IN_ECLIPSE) {
       throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
     }
-    def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
-    def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
-    if (!eclipseJ2sPluginFile.exists()) {
-      def msg = "Eclipse J2S Plugin is not installed (could not find '${eclipseJ2sPlugin}')\nTry running task jalviewjsIDE_copyJ2sPlugin"
+    def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
+    def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
+    if (altPluginsDir != null && file(altPluginsDir).exists()) {
+      eclipseJ2sPluginDirs += altPluginsDir
+    }
+    def foundPlugin = false
+    def j2sPluginFileName = j2sPluginFile.getName()
+    def eclipseJ2sPlugin
+    def eclipseJ2sPluginFile
+    eclipseJ2sPluginDirs.any { dir ->
+      eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
+      eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
+      if (eclipseJ2sPluginFile.exists()) {
+        foundPlugin = true
+        return true
+      }
+    }
+    if (!foundPlugin) {
+      def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
       System.err.println(msg)
       throw new StopExecutionException(msg)
     }
@@ -2655,7 +2879,7 @@ task jalviewjsIDE_checkJ2sPlugin {
       System.err.println(msg)
       throw new StopExecutionException(msg)
     } else {
-      def msg = "Eclipse J2S Plugin is the same as '${j2sPlugin}' (this is good)"
+      def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
       println(msg)
     }
   }
@@ -2715,7 +2939,7 @@ task jalviewjsIDE_PrepareSite {
   description "Sync libs and resources to site dir, but not closure cores"
 
   dependsOn jalviewjsIDE_SyncSiteAll
-  dependsOn cleanJalviewjsTransferSite
+  //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
 }
 
 
diff --git a/doc/JalviewJS-API.md b/doc/JalviewJS-API.md
new file mode 100644 (file)
index 0000000..64cae62
--- /dev/null
@@ -0,0 +1,123 @@
+# public interface JalviewJSApi
+
+## full list of available methods (from JalviewLiteJsApi):
+
+  public boolean addPdbFile(AlignFrame alFrame, String sequenceId, String pdbEntryString, String pdbFile);
+  public String getAlignment(String format);
+  public String getAlignment(String format, String suffix);
+  public String getAlignmentFrom(AlignFrame alf, String format);
+  public String getAlignmentFrom(AlignFrame alf, String format, String suffix);
+  public String getAlignmentOrder();
+  public String getAlignmentOrderFrom(AlignFrame alf);
+  public String getAlignmentOrderFrom(AlignFrame alf, String sep);
+  public String getAnnotation();
+  public String getAnnotationFrom(AlignFrame alf);
+  public Object getAppletParameter(String name, boolean asString);
+  public URL getCodeBase();
+  public URL getDocumentBase();
+  public String getFeatureGroups();
+  public String getFeatureGroupsOfState(boolean visible);
+  public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible);
+  public String getFeatureGroupsOn(AlignFrame alf);
+  public String getFeatures(String format);
+  public String getFeaturesFrom(AlignFrame alf, String format);
+  public Object getFrameForSource(VamsasSource source);
+  public String getParameter(String name);
+  public String getSelectedSequences();
+  public String getSelectedSequences(String sep);
+  public String getSelectedSequencesAsAlignment(String format, String suffix);
+  public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf, String format, String suffix);
+  public String getSelectedSequencesFrom(AlignFrame alf);
+  public String getSelectedSequencesFrom(AlignFrame alf, String sep);
+  public String getSeparator();
+  public AlignViewportI getViewport();
+  public void highlight(String sequenceId, String position, String alignedPosition);
+  public void highlightIn(AlignFrame alf, String sequenceId, String position, String alignedPosition);
+  public AlignFrame loadAlignment(String text, String title);
+  public void loadAnnotation(String annotation);
+  public void loadAnnotationFrom(AlignFrame alf, String annotation);
+  public void loadFeatures(String features, boolean autoenabledisplay);
+  public boolean loadFeaturesFrom(AlignFrame alf, String features, boolean autoenabledisplay);
+  public boolean loadScoreFile(String sScoreFile) throws IOException;
+  public void newFeatureSettings();
+  public void newStructureView(PDBEntry pdb, SequenceI[] seqs, String[] chains, DataSourceType protocol);
+  public Object openPcaPanel(AlignFrame af, String modelName);
+  public Object openTreePanel(AlignFrame af, String treeType, String modelName);
+  public String orderAlignmentBy(AlignFrame alf, String order, String undoName, String sep);
+  public String orderBy(String order, String undoName);
+  public String orderBy(String order, String undoName, String sep);
+  public Object parseArguments(String[] args);
+  public boolean parseFeaturesFile(String param, DataSourceType protocol);
+  public void removeSelectionListener(AlignFrame af, String listener);
+  public void scrollViewToColumnIn(AlignFrame alf, String leftHandColumn);
+  public void scrollViewToIn(AlignFrame alf, String topRow, String leftHandColumn);
+  public void scrollViewToRowIn(AlignFrame alf, String topRow);
+  public void select(String sequenceIds, String columns);
+  public void select(String sequenceIds, String columns, String sep);
+  public void selectIn(AlignFrame alf, String sequenceIds, String columns);
+  public void selectIn(AlignFrame alf, String sequenceIds, String columns, String sep);
+  public void setFeatureGroupState(String groups, boolean state);
+  public void setFeatureGroupState(String[] groups, boolean state);
+  public void setFeatureGroupStateOn(AlignFrame alf, String groups, boolean state);
+  public void setSelectionListener(AlignFrame af, String listener);
+  public void setSelectionListener(String listener);
+  public void setSeparator(String separator);
+  public void showOverview();
+  public void updateForAnnotations();
+
+## addition available methods (from JalviewLite):
+
+  public static String getBuildDate()
+  public static String getInstallation()
+  public static String getVersion()
+
+
+
+## proposed alias list:
+- remove overloaded methods
+- indicate null options
+- use standard arrays; no need for special separators
+- possibly return more actual objects, not just strings
+- moves AlignFrame to last parameter, as it is likely to be unnecessary
+
+public boolean addPdbFile(String sequenceId, String pdbId, String pdbFile, AlignFrame alFrame);
+public String getAlignment(String format, boolean addSuffix, AlignFrame alf);
+public String[] getAlignmentOrder(AlignFrame alf);
+public String getAnnotation(AlignFrame alf);
+public URL getCodeBase();
+public URL getDocumentBase();
+public String[] getFeatureGroups(AlignFrame alf);
+public String[] getFeatureGroupsOfState(boolean visible, AlignFrame alf);
+public String getFeatures(String format, AlignFrame alf);
+public String getParameter(String name);
+public Object getParameterAsObject(String name);
+public SequenceI[] getSelectedSequences(AlignFrame alf);
+public AlignFrame newView();
+public AlignFrame newView(String name);
+public AlignFrame newViewFrom(AlignFrame alf);
+public AlignFrame newViewFrom(AlignFrame alf, String name);
+public String getSelectedSequencesAsAlignment(String format, boolean addSuffix, AlignFrame alf);
+public void highlight(String sequenceId, String position, String alignedPosition, AlignFrame alf);
+public AlignFrame loadAlignment(String data, String title, int width, int height);
+public void loadAnnotation(String annotation, AlignFrame alf);
+public boolean loadFeatures(String features, boolean autoenabledisplay, AlignFrame alf);
+public boolean loadScoreFile(String sScoreFile, AlignFrame alf);
+public Object openPcaPanel(String modelName, AlignFrame alf);
+public Object openTreePanel(String treeType, String modelName, AlignFrame alf);
+public boolean orderAlignment(String[] ids, String undoName, AlignFrame alf);
+public Object parseArguments(String[] args);
+public void removeSelectionListener(String listener, AlignFrame af);
+public void scrollViewTo(int topRow, int leftHandColumn, AlignFrame alf);
+public void select(String[] sequenceIds, String[] columns, AlignFrame alf);
+public void setFeatureGroupState(String[] groups, boolean state, AlignFrame alf);
+public void setSelectionListener(String listener, AlignFrame alf);
+public void showOverview();
+public void showStructure(String pdbID, String fileType, AlignFrame alf);
+
+# unknown methods/shouldn't be in interface?
+
+  public Object getFrameForSource(VamsasSource source);
+  public void setSeparator(String separator);
+  
\ No newline at end of file
diff --git a/doc/JalviewJS-startupParams.md b/doc/JalviewJS-startupParams.md
new file mode 100644 (file)
index 0000000..9ff3352
--- /dev/null
@@ -0,0 +1,92 @@
+## JalviewJS startup parameters
+
+TODO -- go through these
+
+# parameters -- from jalview.bin.AppletParams
+
+  private final static String[] params = { 
+      "alignpdbfiles",
+      "ANNOTATIONCOLOUR_MAX", "ANNOTATIONCOLOUR_MIN",
+      "annotations",
+      "APPLICATION_URL", "automaticScrolling", "centrecolumnlabels",
+      "debug", "defaultColour", "defaultColourNuc", "defaultColourProt",
+      "embedded", "enableSplitFrame", "externalstructureviewer", "features",
+      "file", "file2", "format", "heightScale", "hidefeaturegroups",
+      "jalviewhelpurl", "jnetfile", "jpredfile", "label", "linkLabel_",
+      "linkLabel_1", "linkURL_", "nojmol", "normaliseLogo",
+      "normaliseSequenceLogo", "oninit", "PDBFILE", "PDBSEQ",
+      "relaxedidmatch", "resolvetocodebase", "RGB", "scaleProteinAsCdna",
+      "scoreFile", "separator", "sequence", "showAnnotation", "showbutton",
+      "showConsensus", "showConsensusHistogram", "showConservation",
+      "showfeaturegroups", "showFeatureSettings", "showFullId",
+      "showGroupConsensus", "showGroupConservation", "showOccupancy",
+      "showQuality", "showSequenceLogo", "showTreeBootstraps",
+      "showTreeDistances", "showUnconserved", "showUnlinkedTreeNodes",
+      "sortBy", "sortByTree", "tree", "treeFile", "upperCase",
+      "userDefinedColour", "widthScale", "windowHeight", "windowWidth",
+      "wrap", };
+
+
+
+
+# arguments -- from jalview.bin.ArgsParser
+
+  public static final String NOCALCULATION = "nocalculation";
+
+  public static final String NOMENUBAR = "nomenubar";
+
+  public static final String NOSTATUS = "nostatus";
+
+  public static final String SHOWOVERVIEW = "showoverview";
+
+  //
+  public static final String ANNOTATIONS = "annotations";
+
+  public static final String COLOUR = "colour";
+
+  public static final String FEATURES = "features";
+
+  public static final String GROOVY = "groovy";
+
+  public static final String GROUPS = "groups";
+
+  public static final String HEADLESS = "headless";
+
+  public static final String JABAWS = "jabaws";
+
+  public static final String NOANNOTATION = "no-annotation";
+
+  public static final String NOANNOTATION2 = "noannotation"; // BH 2019.05.07
+
+  public static final String NODISPLAY = "nodisplay";
+
+  public static final String NOGUI = "nogui";
+
+  public static final String NONEWS = "nonews";
+
+  public static final String NOQUESTIONNAIRE = "noquestionnaire";
+
+  public static final String NOSORTBYTREE = "nosortbytree";
+
+  public static final String NOUSAGESTATS = "nousagestats";
+
+  public static final String OPEN = "open";
+
+  public static final String OPEN2 = "open2"; // BH added -- for applet
+                                              // compatibility; not fully
+                                              // implemented
+
+  public static final String PROPS = "props";
+
+  public static final String QUESTIONNAIRE = "questionnaire";
+
+  public static final String SETPROP = "setprop";
+
+  public static final String SORTBYTREE = "sortbytree";
+
+  public static final String TREE = "tree";
+
+  public static final String VDOC = "vdoc";
+
+  public static final String VSESS = "vsess";
+
diff --git a/doc/JalviewJS-startupParams.xlsx b/doc/JalviewJS-startupParams.xlsx
new file mode 100644 (file)
index 0000000..d32fc48
Binary files /dev/null and b/doc/JalviewJS-startupParams.xlsx differ
index cc5eb30..2ca2641 100644 (file)
+<html>
 <!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>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-  <meta http-equiv="Content-Style-Type" content="text/css" />
-  <meta name="generator" content="pandoc" />
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta http-equiv="Content-Style-Type" content="text/css" />
+    <meta name="generator" content="flexmark" />
   <title>Building Jalview from Source</title>
-  <style type="text/css">code{white-space: pre;}</style>
-  <style type="text/css">
-div.sourceCode { overflow-x: auto; }
-table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
-  margin: 0; padding: 0; vertical-align: baseline; border: none; }
-table.sourceCode { width: 100%; line-height: 100%; }
-td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
-td.sourceCode { padding-left: 5px; }
-code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
-code > span.dt { color: #902000; } /* DataType */
-code > span.dv { color: #40a070; } /* DecVal */
-code > span.bn { color: #40a070; } /* BaseN */
-code > span.fl { color: #40a070; } /* Float */
-code > span.ch { color: #4070a0; } /* Char */
-code > span.st { color: #4070a0; } /* String */
-code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
-code > span.ot { color: #007020; } /* Other */
-code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
-code > span.fu { color: #06287e; } /* Function */
-code > span.er { color: #ff0000; font-weight: bold; } /* Error */
-code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
-code > span.cn { color: #880000; } /* Constant */
-code > span.sc { color: #4070a0; } /* SpecialChar */
-code > span.vs { color: #4070a0; } /* VerbatimString */
-code > span.ss { color: #bb6688; } /* SpecialString */
-code > span.im { } /* Import */
-code > span.va { color: #19177c; } /* Variable */
-code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
-code > span.op { color: #666666; } /* Operator */
-code > span.bu { } /* BuiltIn */
-code > span.ex { } /* Extension */
-code > span.pp { color: #bc7a00; } /* Preprocessor */
-code > span.at { color: #7d9029; } /* Attribute */
-code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
-code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
-code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
-code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
-  </style>
-  <style>
-  @font-face {
-    font-family: octicons-link;
-    src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff');
-  }
-  
-  body {
-    -webkit-text-size-adjust: 100%;
-    text-size-adjust: 100%;
-    color: #333;
-    font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
-    font-size: 16px;
-    line-height: 1.6;
-    word-wrap: break-word;
-    width: 728px;
-    max-width: 99%;
-    box-sizing: border-box;
-    padding: 30px 30px 8rem 30px;
-    margin-left: auto;
-    margin-right: auto;
-  }
-  
-  body a {
-    background-color: transparent;
-  }
-  
-  body a:active,
-  body a:hover {
-    outline: 0;
-  }
-  
-  body strong {
-    font-weight: bold;
-  }
-  
-  body h1 {
-    font-size: 2em;
-    margin: 0.67em 0;
-  }
-  
-  body img {
-    border: 0;
-  }
-  
-  body hr {
-    box-sizing: content-box;
-    height: 0;
-  }
-  
-  body pre {
-    overflow: auto;
-  }
-  
-  body code,
-  body kbd,
-  body pre {
-    font-family: monospace, monospace;
-    font-size: 1em;
-  }
-  
-  body input {
-    color: inherit;
-    font: inherit;
-    margin: 0;
-  }
-  
-  body html input[disabled] {
-    cursor: default;
-  }
-  
-  body input {
-    line-height: normal;
-  }
-  
-  body input[type="checkbox"] {
-    box-sizing: border-box;
-    padding: 0;
-  }
-  
-  body table {
-    border-collapse: collapse;
-    border-spacing: 0;
-  }
-  
-  body td,
-  body th {
-    padding: 0;
-  }
-  
-  body * {
-    box-sizing: border-box;
-  }
-  
-  body input {
-    font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
-  }
-  
-  body a {
-    color: #4078c0;
-    text-decoration: none;
-  }
-  
-  body a:hover,
-  body a:active {
-    text-decoration: underline;
-  }
-  
-  body hr {
-    height: 0;
-    margin: 15px 0;
-    overflow: hidden;
-    background: transparent;
-    border: 0;
-    border-bottom: 1px solid #ddd;
-  }
-  
-  body hr:before {
-    display: table;
-    content: "";
-  }
-  
-  body hr:after {
-    display: table;
-    clear: both;
-    content: "";
-  }
-  
-  body h1,
-  body h2,
-  body h3,
-  body h4,
-  body h5,
-  body h6 {
-    margin-top: 15px;
-    margin-bottom: 15px;
-    line-height: 1.1;
-  }
-  
-  body h1 {
-    font-size: 30px;
-  }
-  
-  body h2 {
-    font-size: 21px;
-  }
-  
-  body h3 {
-    font-size: 16px;
-  }
-  
-  body h4 {
-    font-size: 14px;
-  }
-  
-  body h5 {
-    font-size: 12px;
-  }
-  
-  body h6 {
-    font-size: 11px;
-  }
-  
-  body blockquote {
-    margin: 0;
-  }
-  
-  body ul,
-  body ol {
-    padding: 0;
-    margin-top: 0;
-    margin-bottom: 0;
-  }
-  
-  body ol ol,
-  body ul ol {
-    list-style-type: lower-roman;
-  }
-  
-  body ul ul ol,
-  body ul ol ol,
-  body ol ul ol,
-  body ol ol ol {
-    list-style-type: lower-alpha;
-  }
-  
-  body dd {
-    margin-left: 0;
-  }
-  
-  body code {
-    font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
-    font-size: 12px;
-  }
-  
-  body pre {
-    margin-top: 0;
-    margin-bottom: 0;
-    font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
-  }
-  
-  body .select::-ms-expand {
-    opacity: 0;
-  }
-  
-  body .octicon {
-    font: normal normal normal 16px/1 octicons-link;
-    display: inline-block;
-    text-decoration: none;
-    text-rendering: auto;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-    -webkit-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-  }
-  
-  body .octicon-link:before {
-    content: '\f05c';
-  }
-  
-  body:before {
-    display: table;
-    content: "";
-  }
-  
-  body:after {
-    display: table;
-    clear: both;
-    content: "";
-  }
-  
-  body>*:first-child {
-    margin-top: 0 !important;
-  }
-  
-  body>*:last-child {
-    margin-bottom: 0 !important;
-  }
-  
-  body a:not([href]) {
-    color: inherit;
-    text-decoration: none;
-  }
-  
-  body .anchor {
-    display: inline-block;
-    padding-right: 2px;
-    margin-left: -18px;
-  }
-  
-  body .anchor:focus {
-    outline: none;
-  }
-  
-  body h1,
-  body h2,
-  body h3,
-  body h4,
-  body h5,
-  body h6 {
-    margin-top: 1em;
-    margin-bottom: 16px;
-    font-weight: bold;
-    line-height: 1.4;
-  }
-  
-  body h1 .octicon-link,
-  body h2 .octicon-link,
-  body h3 .octicon-link,
-  body h4 .octicon-link,
-  body h5 .octicon-link,
-  body h6 .octicon-link {
-    color: #000;
-    vertical-align: middle;
-    visibility: hidden;
-  }
-  
-  body h1:hover .anchor,
-  body h2:hover .anchor,
-  body h3:hover .anchor,
-  body h4:hover .anchor,
-  body h5:hover .anchor,
-  body h6:hover .anchor {
-    text-decoration: none;
-  }
-  
-  body h1:hover .anchor .octicon-link,
-  body h2:hover .anchor .octicon-link,
-  body h3:hover .anchor .octicon-link,
-  body h4:hover .anchor .octicon-link,
-  body h5:hover .anchor .octicon-link,
-  body h6:hover .anchor .octicon-link {
-    visibility: visible;
-  }
-  
-  body h1 {
-    padding-bottom: 0.3em;
-    font-size: 1.75em;
-    line-height: 1.2;
-  }
-  
-  body h1 .anchor {
-    line-height: 1;
-  }
-  
-  body h2 {
-    padding-bottom: 0.3em;
-    font-size: 1.5em;
-    line-height: 1.225;
-  }
-  
-  body h2 .anchor {
-    line-height: 1;
-  }
-  
-  body h3 {
-    font-size: 1.25em;
-    line-height: 1.43;
-  }
-  
-  body h3 .anchor {
-    line-height: 1.2;
-  }
-  
-  body h4 {
-    font-size: 1em;
-  }
-  
-  body h4 .anchor {
-    line-height: 1.2;
-  }
-  
-  body h5 {
-    font-size: 1em;
-  }
-  
-  body h5 .anchor {
-    line-height: 1.1;
-  }
-  
-  body h6 {
-    font-size: 1em;
-    color: #777;
-  }
-  
-  body h6 .anchor {
-    line-height: 1.1;
-  }
-  
-  body p,
-  body blockquote,
-  body ul,
-  body ol,
-  body dl,
-  body table,
-  body pre {
-    margin-top: 0;
-    margin-bottom: 16px;
-  }
-  
-  body hr {
-    height: 4px;
-    padding: 0;
-    margin: 16px 0;
-    background-color: #e7e7e7;
-    border: 0 none;
-  }
-  
-  body ul,
-  body ol {
-    padding-left: 2em;
-  }
-  
-  body ul ul,
-  body ul ol,
-  body ol ol,
-  body ol ul {
-    margin-top: 0;
-    margin-bottom: 0;
-  }
-  
-  body li>p {
-    margin-top: 16px;
-  }
-  
-  body dl {
-    padding: 0;
-  }
-  
-  body dl dt {
-    padding: 0;
-    margin-top: 16px;
-    font-size: 1em;
-    font-style: italic;
-    font-weight: bold;
-  }
-  
-  body dl dd {
-    padding: 0 16px;
-    margin-bottom: 16px;
-  }
-  
-  body blockquote {
-    padding: 0 15px;
-    color: #777;
-    border-left: 4px solid #ddd;
-  }
-  
-  body blockquote>:first-child {
-    margin-top: 0;
-  }
-  
-  body blockquote>:last-child {
-    margin-bottom: 0;
-  }
-  
-  body table {
-    display: block;
-    width: 100%;
-    overflow: auto;
-    word-break: normal;
-    word-break: keep-all;
-  }
-  
-  body table th {
-    font-weight: bold;
-  }
-  
-  body table th,
-  body table td {
-    padding: 6px 13px;
-    border: 1px solid #ddd;
-  }
-  
-  body table tr {
-    background-color: #fff;
-    border-top: 1px solid #ccc;
-  }
-  
-  body table tr:nth-child(2n) {
-    background-color: #f8f8f8;
-  }
-  
-  body img {
-    max-width: 100%;
-    box-sizing: content-box;
-    background-color: #fff;
-  }
-  
-  body code {
-    padding: 0;
-    padding-top: 0;
-    padding-bottom: 0;
-    margin: 0;
-    font-size: 85%;
-    background-color: rgba(0,0,0,0.04);
-    border-radius: 3px;
-  }
-  
-  body code:before,
-  body code:after {
-    letter-spacing: -0.2em;
-    content: "\00a0";
-  }
-  
-  body pre>code {
-    padding: 0;
-    margin: 0;
-    font-size: 100%;
-    word-break: normal;
-    white-space: pre;
-    background: transparent;
-    border: 0;
-  }
-  
-  body .highlight {
-    margin-bottom: 16px;
-  }
-  
-  body .highlight pre,
-  body pre {
-    padding: 16px;
-    overflow: auto;
-    font-size: 85%;
-    line-height: 1.45;
-    background-color: #f7f7f7;
-    border-radius: 3px;
-  }
-  
-  body .highlight pre {
-    margin-bottom: 0;
-    word-break: normal;
-  }
-  
-  body pre {
-    word-wrap: normal;
-  }
-  
-  body pre code {
-    display: inline;
-    max-width: initial;
-    padding: 0;
-    margin: 0;
-    overflow: initial;
-    line-height: inherit;
-    word-wrap: normal;
-    background-color: transparent;
-    border: 0;
-  }
-  
-  body pre code:before,
-  body pre code:after {
-    content: normal;
-  }
-  
-  body kbd {
-    display: inline-block;
-    padding: 3px 5px;
-    font-size: 11px;
-    line-height: 10px;
-    color: #555;
-    vertical-align: middle;
-    background-color: #fcfcfc;
-    border: solid 1px #ccc;
-    border-bottom-color: #bbb;
-    border-radius: 3px;
-    box-shadow: inset 0 -1px 0 #bbb;
-  }
-  
-  body .pl-c {
-    color: #969896;
-  }
-  
-  body .pl-c1,
-  body .pl-s .pl-v {
-    color: #0086b3;
-  }
-  
-  body .pl-e,
-  body .pl-en {
-    color: #795da3;
-  }
-  
-  body .pl-s .pl-s1,
-  body .pl-smi {
-    color: #333;
-  }
-  
-  body .pl-ent {
-    color: #63a35c;
-  }
-  
-  body .pl-k {
-    color: #a71d5d;
-  }
-  
-  body .pl-pds,
-  body .pl-s,
-  body .pl-s .pl-pse .pl-s1,
-  body .pl-sr,
-  body .pl-sr .pl-cce,
-  body .pl-sr .pl-sra,
-  body .pl-sr .pl-sre {
-    color: #183691;
-  }
-  
-  body .pl-v {
-    color: #ed6a43;
-  }
-  
-  body .pl-id {
-    color: #b52a1d;
-  }
-  
-  body .pl-ii {
-    background-color: #b52a1d;
-    color: #f8f8f8;
-  }
-  
-  body .pl-sr .pl-cce {
-    color: #63a35c;
-    font-weight: bold;
-  }
-  
-  body .pl-ml {
-    color: #693a17;
-  }
-  
-  body .pl-mh,
-  body .pl-mh .pl-en,
-  body .pl-ms {
-    color: #1d3e81;
-    font-weight: bold;
-  }
-  
-  body .pl-mq {
-    color: #008080;
-  }
-  
-  body .pl-mi {
-    color: #333;
-    font-style: italic;
-  }
-  
-  body .pl-mb {
-    color: #333;
-    font-weight: bold;
-  }
-  
-  body .pl-md {
-    background-color: #ffecec;
-    color: #bd2c00;
-  }
-  
-  body .pl-mi1 {
-    background-color: #eaffea;
-    color: #55a532;
-  }
-  
-  body .pl-mdr {
-    color: #795da3;
-    font-weight: bold;
-  }
-  
-  body .pl-mo {
-    color: #1d3e81;
-  }
-  
-  body kbd {
-    display: inline-block;
-    padding: 3px 5px;
-    font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
-    line-height: 10px;
-    color: #555;
-    vertical-align: middle;
-    background-color: #fcfcfc;
-    border: solid 1px #ccc;
-    border-bottom-color: #bbb;
-    border-radius: 3px;
-    box-shadow: inset 0 -1px 0 #bbb;
-  }
-  
-  body .task-list-item {
-    list-style-type: none;
-  }
-  
-  body .task-list-item+.task-list-item {
-    margin-top: 3px;
-  }
-  
-  body .task-list-item input {
-    margin: 0 0.35em 0.25em -1.6em;
-    vertical-align: middle;
-  }
-  
-  body :checked+.radio-label {
-    z-index: 1;
-    position: relative;
-    border-color: #4078c0;
-  }
-  </style>
+    <style type="text/css">code{white-space: pre;}</style>
+<style>
+@font-face {
+  font-family: octicons-link;
+  src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff');
+}
+
+body {
+  -webkit-text-size-adjust: 100%;
+  text-size-adjust: 100%;
+  color: #333;
+  font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+  font-size: 16px;
+  line-height: 1.6;
+  word-wrap: break-word;
+  width: 728px;
+  max-width: 99%;
+  box-sizing: border-box;
+  padding: 30px 30px 8rem 30px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+body a {
+  background-color: transparent;
+}
+
+body a:active,
+body a:hover {
+  outline: 0;
+}
+
+body strong {
+  font-weight: bold;
+}
+
+body h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+body img {
+  border: 0;
+}
+
+body hr {
+  box-sizing: content-box;
+  height: 0;
+}
+
+body pre {
+  overflow: auto;
+}
+
+body code,
+body kbd,
+body pre {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+
+body input {
+  color: inherit;
+  font: inherit;
+  margin: 0;
+}
+
+body html input[disabled] {
+  cursor: default;
+}
+
+body input {
+  line-height: normal;
+}
+
+body input[type="checkbox"] {
+  box-sizing: border-box;
+  padding: 0;
+}
+
+body table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+body td,
+body th {
+  padding: 0;
+}
+
+body * {
+  box-sizing: border-box;
+}
+
+body input {
+  font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+}
+
+body a {
+  color: #4078c0;
+  text-decoration: none;
+}
+
+body a:hover,
+body a:active {
+  text-decoration: underline;
+}
+
+body hr {
+  height: 0;
+  margin: 15px 0;
+  overflow: hidden;
+  background: transparent;
+  border: 0;
+  border-bottom: 1px solid #ddd;
+}
+
+body hr:before {
+  display: table;
+  content: "";
+}
+
+body hr:after {
+  display: table;
+  clear: both;
+  content: "";
+}
+
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6 {
+  margin-top: 15px;
+  margin-bottom: 15px;
+  line-height: 1.1;
+}
+
+body h1 {
+  font-size: 30px;
+}
+
+body h2 {
+  font-size: 21px;
+}
+
+body h3 {
+  font-size: 16px;
+}
+
+body h4 {
+  font-size: 14px;
+}
+
+body h5 {
+  font-size: 12px;
+}
+
+body h6 {
+  font-size: 11px;
+}
+
+body blockquote {
+  margin: 0;
+}
+
+body ul,
+body ol {
+  padding: 0;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+body ol ol,
+body ul ol {
+  list-style-type: lower-roman;
+}
+
+body ul ul ol,
+body ul ol ol,
+body ol ul ol,
+body ol ol ol {
+  list-style-type: lower-alpha;
+}
+
+body dd {
+  margin-left: 0;
+}
+
+body code {
+  font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  font-size: 12px;
+}
+
+body pre {
+  margin-top: 0;
+  margin-bottom: 0;
+  font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+}
+
+body .select::-ms-expand {
+  opacity: 0;
+}
+
+body .octicon {
+  font: normal normal normal 16px/1 octicons-link;
+  display: inline-block;
+  text-decoration: none;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+body .octicon-link:before {
+  content: '\f05c';
+}
+
+body:before {
+  display: table;
+  content: "";
+}
+
+body:after {
+  display: table;
+  clear: both;
+  content: "";
+}
+
+body>*:first-child {
+  margin-top: 0 !important;
+}
+
+body>*:last-child {
+  margin-bottom: 0 !important;
+}
+
+body a:not([href]) {
+  color: inherit;
+  text-decoration: none;
+}
+
+body .anchor {
+  display: inline-block;
+  padding-right: 2px;
+  margin-left: -18px;
+}
+
+body .anchor:focus {
+  outline: none;
+}
+
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6 {
+  margin-top: 1em;
+  margin-bottom: 16px;
+  font-weight: bold;
+  line-height: 1.4;
+}
+
+body h1 .octicon-link,
+body h2 .octicon-link,
+body h3 .octicon-link,
+body h4 .octicon-link,
+body h5 .octicon-link,
+body h6 .octicon-link {
+  color: #000;
+  vertical-align: middle;
+  visibility: hidden;
+}
+
+body h1:hover .anchor,
+body h2:hover .anchor,
+body h3:hover .anchor,
+body h4:hover .anchor,
+body h5:hover .anchor,
+body h6:hover .anchor {
+  text-decoration: none;
+}
+
+body h1:hover .anchor .octicon-link,
+body h2:hover .anchor .octicon-link,
+body h3:hover .anchor .octicon-link,
+body h4:hover .anchor .octicon-link,
+body h5:hover .anchor .octicon-link,
+body h6:hover .anchor .octicon-link {
+  visibility: visible;
+}
+
+body h1 {
+  padding-bottom: 0.3em;
+  font-size: 1.75em;
+  line-height: 1.2;
+}
+
+body h1 .anchor {
+  line-height: 1;
+}
+
+body h2 {
+  padding-bottom: 0.3em;
+  font-size: 1.5em;
+  line-height: 1.225;
+}
+
+body h2 .anchor {
+  line-height: 1;
+}
+
+body h3 {
+  font-size: 1.25em;
+  line-height: 1.43;
+}
+
+body h3 .anchor {
+  line-height: 1.2;
+}
+
+body h4 {
+  font-size: 1em;
+}
+
+body h4 .anchor {
+  line-height: 1.2;
+}
+
+body h5 {
+  font-size: 1em;
+}
+
+body h5 .anchor {
+  line-height: 1.1;
+}
+
+body h6 {
+  font-size: 1em;
+  color: #777;
+}
+
+body h6 .anchor {
+  line-height: 1.1;
+}
+
+body p,
+body blockquote,
+body ul,
+body ol,
+body dl,
+body table,
+body pre {
+  margin-top: 0;
+  margin-bottom: 16px;
+}
+
+body hr {
+  height: 4px;
+  padding: 0;
+  margin: 16px 0;
+  background-color: #e7e7e7;
+  border: 0 none;
+}
+
+body ul,
+body ol {
+  padding-left: 2em;
+}
+
+body ul ul,
+body ul ol,
+body ol ol,
+body ol ul {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+body li>p {
+  margin-top: 16px;
+}
+
+body dl {
+  padding: 0;
+}
+
+body dl dt {
+  padding: 0;
+  margin-top: 16px;
+  font-size: 1em;
+  font-style: italic;
+  font-weight: bold;
+}
+
+body dl dd {
+  padding: 0 16px;
+  margin-bottom: 16px;
+}
+
+body blockquote {
+  padding: 0 15px;
+  color: #777;
+  border-left: 4px solid #ddd;
+}
+
+body blockquote>:first-child {
+  margin-top: 0;
+}
+
+body blockquote>:last-child {
+  margin-bottom: 0;
+}
+
+body table {
+  display: block;
+  width: 100%;
+  overflow: auto;
+  word-break: normal;
+  word-break: keep-all;
+}
+
+body table th {
+  font-weight: bold;
+}
+
+body table th,
+body table td {
+  padding: 6px 13px;
+  border: 1px solid #ddd;
+}
+
+body table tr {
+  background-color: #fff;
+  border-top: 1px solid #ccc;
+}
+
+body table tr:nth-child(2n) {
+  background-color: #f8f8f8;
+}
+
+body img {
+  max-width: 100%;
+  box-sizing: content-box;
+  background-color: #fff;
+}
+
+body code {
+  padding: 0;
+  padding-top: 0;
+  padding-bottom: 0;
+  margin: 0;
+  font-size: 85%;
+  background-color: rgba(0,0,0,0.04);
+  border-radius: 3px;
+}
+
+body code:before,
+body code:after {
+  letter-spacing: -0.2em;
+  content: "\00a0";
+}
+
+body pre>code {
+  padding: 0;
+  margin: 0;
+  font-size: 100%;
+  word-break: normal;
+  white-space: pre;
+  background: transparent;
+  border: 0;
+}
+
+body .highlight {
+  margin-bottom: 16px;
+}
+
+body .highlight pre,
+body pre {
+  padding: 16px;
+  overflow: auto;
+  font-size: 85%;
+  line-height: 1.45;
+  background-color: #f7f7f7;
+  border-radius: 3px;
+}
+
+body .highlight pre {
+  margin-bottom: 0;
+  word-break: normal;
+}
+
+body pre {
+  word-wrap: normal;
+}
+
+body pre code {
+  display: inline;
+  max-width: initial;
+  padding: 0;
+  margin: 0;
+  overflow: initial;
+  line-height: inherit;
+  word-wrap: normal;
+  background-color: transparent;
+  border: 0;
+}
+
+body pre code:before,
+body pre code:after {
+  content: normal;
+}
+
+body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font-size: 11px;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+body .pl-c {
+  color: #969896;
+}
+
+body .pl-c1,
+body .pl-s .pl-v {
+  color: #0086b3;
+}
+
+body .pl-e,
+body .pl-en {
+  color: #795da3;
+}
+
+body .pl-s .pl-s1,
+body .pl-smi {
+  color: #333;
+}
+
+body .pl-ent {
+  color: #63a35c;
+}
+
+body .pl-k {
+  color: #a71d5d;
+}
+
+body .pl-pds,
+body .pl-s,
+body .pl-s .pl-pse .pl-s1,
+body .pl-sr,
+body .pl-sr .pl-cce,
+body .pl-sr .pl-sra,
+body .pl-sr .pl-sre {
+  color: #183691;
+}
+
+body .pl-v {
+  color: #ed6a43;
+}
+
+body .pl-id {
+  color: #b52a1d;
+}
+
+body .pl-ii {
+  background-color: #b52a1d;
+  color: #f8f8f8;
+}
+
+body .pl-sr .pl-cce {
+  color: #63a35c;
+  font-weight: bold;
+}
+
+body .pl-ml {
+  color: #693a17;
+}
+
+body .pl-mh,
+body .pl-mh .pl-en,
+body .pl-ms {
+  color: #1d3e81;
+  font-weight: bold;
+}
+
+body .pl-mq {
+  color: #008080;
+}
+
+body .pl-mi {
+  color: #333;
+  font-style: italic;
+}
+
+body .pl-mb {
+  color: #333;
+  font-weight: bold;
+}
+
+body .pl-md {
+  background-color: #ffecec;
+  color: #bd2c00;
+}
+
+body .pl-mi1 {
+  background-color: #eaffea;
+  color: #55a532;
+}
+
+body .pl-mdr {
+  color: #795da3;
+  font-weight: bold;
+}
+
+body .pl-mo {
+  color: #1d3e81;
+}
+
+body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+body .task-list-item {
+  list-style-type: none;
+}
+
+body .task-list-item+.task-list-item {
+  margin-top: 3px;
+}
+
+body .task-list-item input {
+  margin: 0 0.35em 0.25em -1.6em;
+  vertical-align: middle;
+}
+
+body :checked+.radio-label {
+  z-index: 1;
+  position: relative;
+  border-color: #4078c0;
+}
+</style>
 </head>
-<body>
-<div id="TOC">
+  <body>
 <ul>
-<li><a href="#building-jalview-from-source">Building Jalview from Source</a><ul>
 <li><a href="#tldr">tl;dr</a></li>
-<li><a href="#setting-up">Setting up</a><ul>
+<li><a href="#setting-up">Setting up</a>
+<ul>
 <li><a href="#java-11-compliant-jdk">Java 11 compliant JDK</a></li>
 <li><a href="#gradle-and-git">gradle and git</a></li>
-</ul></li>
-<li><a href="#downloading-the-jalview-source-tree">Downloading the Jalview source tree</a><ul>
+</ul>
+</li>
+<li><a href="#downloading-the-jalview-source-tree">Downloading the Jalview source tree</a>
+<ul>
 <li><a href="#whats-in-the-source-tree">What's in the source tree?</a></li>
-</ul></li>
-<li><a href="#building-jalview">Building Jalview</a><ul>
+</ul>
+</li>
+<li><a href="#building-jalview">Building Jalview</a>
+<ul>
 <li><a href="#minimal-jalview-build">Minimal Jalview Build</a></li>
 <li><a href="#jalview-in-a-jar-file">Jalview in a Jar File</a></li>
 <li><a href="#distributed-jar-files">Distributed Jar Files</a></li>
@@ -726,18 +693,19 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
 <li><a href="#building-the-getdown-launcher">Building the <code>getdown</code> launcher</a></li>
 <li><a href="#running-tests">Running tests</a></li>
 <li><a href="#installer-packaging-with-install4j">Installer packaging with <em>install4j</em></a></li>
-</ul></li>
+</ul>
+</li>
 <li><a href="#gradle-properties">Gradle properties</a></li>
 <li><a href="#enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</a></li>
-<li><a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a><ul>
+<li><a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a>
+<ul>
 <li><a href="#installing-eclipse-ide">Installing Eclipse IDE</a></li>
 <li><a href="#importing-jalview-as-an-eclipse-project">Importing Jalview as an Eclipse project</a></li>
-</ul></li>
-</ul></li>
 </ul>
-</div>
-<h1 id="building-jalview-from-source">Building Jalview from Source</h1>
-<h2 id="tldr">tl;dr</h2>
+</li>
+</ul>
+<h1 id="building-jalview-from-source"><a href="#building-jalview-from-source" name="building-jalview-from-source" class="anchor"><span class="octicon octicon-link"></span>Building Jalview from Source</a></h1>
+<h2 id="tldr"><a href="#tldr" name="tldr" class="anchor"><span class="octicon octicon-link"></span>tl;dr</a></h2>
 <pre><code># download
 git clone http://source.jalview.org/git/jalview.git
 # compile
@@ -750,411 +718,504 @@ java -jar build/libs/jalview-all-11.jar
 gradle getdown
 # use launcher
 cd getdown/files
-java -jar getdown-launcher.jar . jalview</code></pre>
-<h2 id="setting-up">Setting up</h2>
+java -jar getdown-launcher.jar . jalview
+</code></pre>
+<h2 id="setting-up"><a href="#setting-up" name="setting-up" class="anchor"><span class="octicon octicon-link"></span>Setting up</a></h2>
 <blockquote>
-<p>To get set up using <em>only</em> the Eclipse IDE (<a href="https://www.eclipse.org/" class="uri">https://www.eclipse.org/</a>) then please see the section <a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a></p>
+<p>To get set up using <em>only</em> the Eclipse IDE (<a href="https://www.eclipse.org/">https://www.eclipse.org/</a>) then please see the section <a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a></p>
 </blockquote>
-<p>The method here is described in terms of using a command line. You can easily do this on linux or in a Terminal window in macOS. You can do it in Windows.</p>
+<p>The method here is described in terms of using a command line.  You can easily do this on linux or in a Terminal window in macOS.  You can do it in Windows.</p>
 <ul>
 <li>Java 11 compliant JDK</li>
 <li>gradle 5.2 or above</li>
 <li>git</li>
 </ul>
 <blockquote>
-<p>The versions and installation methods here are just suggestions (which we have tested so are known to work). If you need or wish to use different implementations (particularly you might need a bespoke JDK if you are on an exotic architecture) then the general build instructions should work with any gradle 5+. You should be able to compile the bytecode with any JDK Java 11+. The resulting bytecode (in particular the shadow jar) should be runnable in any JRE Java 1.8+. Remember that because Jalview and the getdown launcher are Java bytecode you can build on one system where you might have gradle, and run on another where you don't (JRE 1.8+ required).</p>
+<p>The versions and installation methods here are just suggestions (which we have tested
+so are known to work).  If you need or wish to use different implementations (particularly
+you might need a bespoke JDK if you are on an exotic architecture) then the general
+build instructions should work with any gradle 5+.  You should be able to compile the
+bytecode with any JDK Java 11+.  The resulting bytecode (in particular the shadow jar)
+should be runnable in any JRE Java 1.8+.  Remember that because Jalview and the getdown launcher
+are Java bytecode you can build on one system where you might have gradle, and run
+on another where you don't (JRE 1.8+ required).</p>
 </blockquote>
-<h3 id="java-11-compliant-jdk">Java 11 compliant JDK</h3>
-<h4 id="all-platforms">All platforms</h4>
-<p>We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: <a href="https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot" class="uri">https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot</a>, either the <em>Installer</em> or <code>.zip</code>/<code>.tar.gz</code> variants whichever you prefer (if you're not sure, choose the <em>Installer</em>).</p>
+<h3 id="java-11-compliant-jdk"><a href="#java-11-compliant-jdk" name="java-11-compliant-jdk" class="anchor"><span class="octicon octicon-link"></span>Java 11 compliant JDK</a></h3>
+<h4 id="all-platforms"><a href="#all-platforms" name="all-platforms" class="anchor"><span class="octicon octicon-link"></span>All platforms</a></h4>
+<p>We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: <a href="https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot">https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot</a>, either the <em>Installer</em> or <code>.zip</code>/<code>.tar.gz</code> variants whichever you prefer (if you're not sure, choose the <em>Installer</em>).</p>
 <blockquote>
-<h5 id="alternativecli-install-of-adoptopenjdk-11">Alternative/CLI install of AdoptOpenJDK 11</h5>
-<p>You can also install adoptopenjdk11 using either <code>brew</code> (macOS), <code>choco</code> (Windows) (see the section on <code>gradle</code> and <code>git</code> for more informaiton on <code>brew</code> and <code>choco</code>) or <code>yum</code> or <code>apt</code> (Linux):</p>
-<h6 id="alternative-for-macos-and-homebrew">alternative for MacOS and Homebrew</h6>
+<h5 id="alternativecli-install-of-adoptopenjdk-11"><a href="#alternativecli-install-of-adoptopenjdk-11" name="alternativecli-install-of-adoptopenjdk-11" class="anchor"><span class="octicon octicon-link"></span>Alternative/CLI install of AdoptOpenJDK 11</a></h5>
+<p>You can also install adoptopenjdk11 using either <code>brew</code> (macOS), <code>choco</code> (Windows)
+(see the section on <code>gradle</code> and <code>git</code> for more informaiton on <code>brew</code> and <code>choco</code>)
+or <code>yum</code> or <code>apt</code> (Linux):</p>
+<h6 id="alternative-for-macos-and-homebrew"><a href="#alternative-for-macos-and-homebrew" name="alternative-for-macos-and-homebrew" class="anchor"><span class="octicon octicon-link"></span>alternative for MacOS and Homebrew</a></h6>
 <pre><code>brew tap adoptopenjdk/openjdk
-brew cask install adoptopenjdk11</code></pre>
-<h6 id="alternative-for-windows-and-chocolatey">alternative for Windows and Chocolatey</h6>
-<pre><code>choco install adoptopenjdk11</code></pre>
-<h6 id="alternative-for-linux-with-yumapt">alternative for Linux with yum/apt</h6>
-<p>see <a href="https://adoptopenjdk.net/installation.html#linux-pkg" class="uri">https://adoptopenjdk.net/installation.html#linux-pkg</a></p>
+brew cask install adoptopenjdk11
+</code></pre>
+<h6 id="alternative-for-windows-and-chocolatey"><a href="#alternative-for-windows-and-chocolatey" name="alternative-for-windows-and-chocolatey" class="anchor"><span class="octicon octicon-link"></span>alternative for Windows and Chocolatey</a></h6>
+<pre><code>choco install adoptopenjdk11
+</code></pre>
+<h6 id="alternative-for-linux-with-yumapt"><a href="#alternative-for-linux-with-yumapt" name="alternative-for-linux-with-yumapt" class="anchor"><span class="octicon octicon-link"></span>alternative for Linux with yum/apt</a></h6>
+<p>see <a href="https://adoptopenjdk.net/installation.html#linux-pkg">https://adoptopenjdk.net/installation.html#linux-pkg</a></p>
 </blockquote>
-<h3 id="gradle-and-git">gradle and git</h3>
+<h3 id="gradle-and-git"><a href="#gradle-and-git" name="gradle-and-git" class="anchor"><span class="octicon octicon-link"></span>gradle and git</a></h3>
 <p>You should be able to install the latest (or sufficiently recent) versions of gradle and git using your OS package manager.</p>
-<h4 id="macos">MacOS</h4>
-<p>we recommend using <code>brew</code>, which can be installed following the instructions at <a href="https://brew.sh/" class="uri">https://brew.sh/</a>. After installing <code>brew</code>, open a Terminal window and type in (using an Administrator privileged user):</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">brew</span> install gradle git</code></pre></div>
+<h4 id="macos"><a href="#macos" name="macos" class="anchor"><span class="octicon octicon-link"></span>MacOS</a></h4>
+<p>we recommend using <code>brew</code>, which can be installed following the instructions at <a href="https://brew.sh/">https://brew.sh/</a>.
+After installing <code>brew</code>, open a Terminal window and type in (using an Administrator privileged user):</p>
+<pre><code class="language-bash">brew install gradle git
+</code></pre>
 <p>or if you aready have them installed but need to upgrade the version:</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">brew</span> upgrade gradle git</code></pre></div>
-<h4 id="windows">Windows</h4>
-<p>we suggest using the <strong>Chocolatey</strong> package manager. See install instructions at <a href="https://chocolatey.org/" class="uri">https://chocolatey.org/</a>, and you will just need</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">choco</span> install gradle
-<span class="ex">choco</span> install git</code></pre></div>
-<p>Alternatively, you could install a real <code>bash</code> shell and install both <code>gradle</code> and <code>git</code> through <code>apt-get</code>. See <a href="https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/" class="uri">https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/</a> for how to install the ubuntu bash shell in Windows 10.</p>
-<p>Another alternative would be to install them separately. For <code>gradle</code> follow the instructions at <a href="https://gradle.org/install/" class="uri">https://gradle.org/install/</a>, and for <code>git</code> here are a couple of suggestions: Git for Windows <a href="https://gitforwindows.org/" class="uri">https://gitforwindows.org/</a>. Getting the individual installs working together on the command line will be trickier so we recommend using Chocolatey or bash.</p>
-<h4 id="linux">Linux</h4>
+<pre><code class="language-bash">brew upgrade gradle git
+</code></pre>
+<h4 id="windows"><a href="#windows" name="windows" class="anchor"><span class="octicon octicon-link"></span>Windows</a></h4>
+<p>we suggest using the <strong>Chocolatey</strong> package manager.  See install instructions at <a href="https://chocolatey.org/">https://chocolatey.org/</a>, and you will just need</p>
+<pre><code class="language-bash">choco install gradle
+choco install git
+</code></pre>
+<p>Alternatively, you could install a real <code>bash</code> shell and install both <code>gradle</code> and <code>git</code> through <code>apt-get</code>.
+See <a href="https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/">https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/</a>
+for how to install the ubuntu bash shell in Windows 10.</p>
+<p>Another alternative would be to install them separately. For <code>gradle</code> follow the instructions at <a href="https://gradle.org/install/">https://gradle.org/install/</a>, and for <code>git</code> here are a couple of suggestions: Git for Windows <a href="https://gitforwindows.org/">https://gitforwindows.org/</a>.
+Getting the individual installs working together on the command line will be trickier
+so we recommend using Chocolatey or bash.</p>
+<h4 id="linux"><a href="#linux" name="linux" class="anchor"><span class="octicon octicon-link"></span>Linux</a></h4>
 <p>this will depend on which distribution you're using.</p>
-<h5 id="for-debian-based-distributions-e.g.-mint-ubuntu-debian">For <em>Debian</em> based distributions (e.g. Mint, Ubuntu, Debian)</h5>
+<h5 id="for-debian-based-distributions-eg-mint-ubuntu-debian"><a href="#for-debian-based-distributions-eg-mint-ubuntu-debian" name="for-debian-based-distributions-eg-mint-ubuntu-debian" class="anchor"><span class="octicon octicon-link"></span>For <em>Debian</em> based distributions (e.g. Mint, Ubuntu, Debian)</a></h5>
 <p>run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"> <span class="fu">sudo</span> apt-get install gradle git</code></pre></div>
-<h5 id="for-rpm-based-distributions-e.g.-fedora-centos-redhat">for RPM-based distributions (e.g. Fedora, CentOS, RedHat)</h5>
+<pre><code class="language-bash"> sudo apt-get install gradle git
+</code></pre>
+<h5 id="for-rpm-based-distributions-eg-fedora-centos-redhat"><a href="#for-rpm-based-distributions-eg-fedora-centos-redhat" name="for-rpm-based-distributions-eg-fedora-centos-redhat" class="anchor"><span class="octicon octicon-link"></span>for RPM-based distributions (e.g. Fedora, CentOS, RedHat)</a></h5>
 <p>run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">sudo</span> yum install gradle git</code></pre></div>
+<pre><code class="language-bash">sudo yum install gradle git
+</code></pre>
 <p>If you have some other version of linux you'll probably be able to work it out!</p>
-<h2 id="downloading-the-jalview-source-tree">Downloading the Jalview source tree</h2>
-<p>This can be done with <code>git</code>. On the command line, change directory to where you want to download Jalview's build-tree top level directory. Then run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">git</span> clone http://source.jalview.org/git/jalview.git</code></pre></div>
-<p>You'll get some progress output and after a minute or two you should have the full Jalview build-tree in the folder <code>jalview</code>.</p>
-<h3 id="whats-in-the-source-tree">What's in the source tree?</h3>
-<p>Jalview is a mature product with its codebase going back many years. As such it doesn't have a folder structure that most new gradle projects would have, so you might not find everything in the place you might expect. Here's a brief description of what you might find in the main folders under the <code>jalview</code> tree.</p>
+<h2 id="downloading-the-jalview-source-tree"><a href="#downloading-the-jalview-source-tree" name="downloading-the-jalview-source-tree" class="anchor"><span class="octicon octicon-link"></span>Downloading the Jalview source tree</a></h2>
+<p>This can be done with <code>git</code>.
+On the command line, change directory to where you want to download Jalview's build-tree
+top level directory.  Then run</p>
+<pre><code class="language-bash">git clone http://source.jalview.org/git/jalview.git
+</code></pre>
+<p>You'll get some progress output and after a minute or two you should have the full
+Jalview build-tree in the folder <code>jalview</code>.</p>
+<h3 id="whats-in-the-source-tree"><a href="#whats-in-the-source-tree" name="whats-in-the-source-tree" class="anchor"><span class="octicon octicon-link"></span>What's in the source tree?</a></h3>
+<p>Jalview is a mature product with its codebase going back many years.  As such it doesn't
+have a folder structure that most new gradle projects would have, so you might not
+find everything in the place you might expect.  Here's a brief description of what
+you might find in the main folders under the <code>jalview</code> tree.</p>
 <p>Within the <code>jalview</code> folder you will find (of possible interest):</p>
 <table>
-<colgroup>
-<col width="16%" />
-<col width="83%" />
-</colgroup>
 <thead>
-<tr class="header">
-<th>dir/ or file</th>
-<th>contains</th>
-</tr>
+<tr><th>dir/ or file</th><th>contains</th></tr>
 </thead>
 <tbody>
-<tr class="odd">
-<td><code>bin/</code></td>
-<td>used by eclipse for compiled classes -- no need to touch this</td>
-</tr>
-<tr class="even">
-<td><code>build/</code></td>
-<td>the gradle build dir</td>
-</tr>
-<tr class="odd">
-<td><code>classes/</code></td>
-<td>contains the compiled Java classes for the Jalview application</td>
-</tr>
-<tr class="even">
-<td><code>dist/</code></td>
-<td>assembled <code>.jar</code> files needed to run Jalview application</td>
-</tr>
-<tr class="odd">
-<td><code>examples/</code></td>
-<td>example input files usable by Jalview</td>
-</tr>
-<tr class="even">
-<td><code>getdown/</code></td>
-<td>the libraries used by the Javliew launcher (getdown)</td>
-</tr>
-<tr class="odd">
-<td><code>getdown/src/</code></td>
-<td>our modified source for <code>getdown</code></td>
-</tr>
-<tr class="even">
-<td><code>getdown/website/</code></td>
-<td>the assembled &quot;download&quot; folder used by getdown for downloads/upgrades</td>
-</tr>
-<tr class="odd">
-<td><code>getdown/files/</code></td>
-<td>the minimal fileset to launch the Jalview launcher, which can then download the rest of the Jalview application</td>
-</tr>
-<tr class="even">
-<td><code>help/</code></td>
-<td>the help documents</td>
-</tr>
-<tr class="odd">
-<td><code>j8lib/</code></td>
-<td>libraries needed to run Jalview under Java 1.8</td>
-</tr>
-<tr class="even">
-<td><code>j11lib/</code></td>
-<td>libraries needed to run Jalivew under Java 11</td>
-</tr>
-<tr class="odd">
-<td><code>resource/</code></td>
-<td>non-java resources used in the Jalview application</td>
-</tr>
-<tr class="even">
-<td><code>src/</code></td>
-<td>the Jalview application source <code>.java</code> files</td>
-</tr>
-<tr class="odd">
-<td><code>test/</code></td>
-<td>Test class source files</td>
-</tr>
-<tr class="even">
-<td><code>utils/</code></td>
-<td>helper applications used in the build process</td>
-</tr>
-<tr class="odd">
-<td><code>utils/install4j/</code></td>
-<td>files used by the packaging tool, install4j</td>
-</tr>
-<tr class="even">
-<td><code>build.gradle</code></td>
-<td>the build file used by gradle</td>
-</tr>
-<tr class="odd">
-<td><code>gradle.properties</code></td>
-<td>configurable properties for the build process</td>
-</tr>
+<tr><td><code>bin/</code></td><td>used by eclipse for compiled classes -- no need to touch this</td></tr>
+<tr><td><code>build/</code></td><td>the gradle build dir</td></tr>
+<tr><td><code>classes/</code></td><td>contains the compiled Java classes for the Jalview application</td></tr>
+<tr><td><code>dist/</code></td><td>assembled <code>.jar</code> files needed to run Jalview application</td></tr>
+<tr><td><code>examples/</code></td><td>example input files usable by Jalview</td></tr>
+<tr><td><code>getdown/</code></td><td>the libraries used by the Javliew launcher (getdown)</td></tr>
+<tr><td><code>getdown/src/</code></td><td>our modified source for <code>getdown</code></td></tr>
+<tr><td><code>getdown/website/</code></td><td>the assembled &quot;download&quot; folder used by getdown for downloads/upgrades</td></tr>
+<tr><td><code>getdown/files/</code></td><td>the minimal fileset to launch the Jalview launcher, which can then download the rest of the Jalview application</td></tr>
+<tr><td><code>help/</code></td><td>the help documents</td></tr>
+<tr><td><code>j8lib/</code></td><td>libraries needed to run Jalview under Java 1.8</td></tr>
+<tr><td><code>j11lib/</code></td><td>libraries needed to run Jalivew under Java 11</td></tr>
+<tr><td><code>resource/</code></td><td>non-java resources used in the Jalview application</td></tr>
+<tr><td><code>src/</code></td><td>the Jalview application source <code>.java</code> files</td></tr>
+<tr><td><code>test/</code></td><td>Test class source files</td></tr>
+<tr><td><code>utils/</code></td><td>helper applications used in the build process</td></tr>
+<tr><td><code>utils/install4j/</code></td><td>files used by the packaging tool, install4j</td></tr>
+<tr><td><code>build.gradle</code></td><td>the build file used by gradle</td></tr>
+<tr><td><code>gradle.properties</code></td><td>configurable properties for the build process</td></tr>
+<tr><td><code>RELEASE</code></td><td>propertyfile configuring JALVIEW_VERSION (from jalview.version) and the release branch (from jalview.release). An alternative file can be specified via JALVIEW_RELEASE_FILE property</td></tr>
 </tbody>
 </table>
 <p>Note that you need a Java 11 JDK to compile Jalview whether your target build is Java 1.8 or Java 11.</p>
-<h2 id="building-jalview">Building Jalview</h2>
-<p>You will need to have the Java 11 <code>javac</code> in your path, or alternatively you can configure gradle to know where this is by putting</p>
-<pre><code>org.gradle.java.home=/path_to_jdk_directory</code></pre>
+<h2 id="building-jalview"><a href="#building-jalview" name="building-jalview" class="anchor"><span class="octicon octicon-link"></span>Building Jalview</a></h2>
+<p>You will need to have the Java 11 <code>javac</code> in your path, or alternatively you can configure
+gradle to know where this is by putting</p>
+<pre><code>org.gradle.java.home=/path_to_jdk_directory
+</code></pre>
 <p>in the <code>gradle.properties</code> file.</p>
 <blockquote>
 <p><em>You may want to see some of the other properties you can change at the end of this document.</em></p>
 </blockquote>
-<h3 id="minimal-jalview-build">Minimal Jalview Build</h3>
+<h3 id="minimal-jalview-build"><a href="#minimal-jalview-build" name="minimal-jalview-build" class="anchor"><span class="octicon octicon-link"></span>Minimal Jalview Build</a></h3>
 <p>To compile the necessary class files, just run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> compileJava</code></pre></div>
-<p>to compile the classes into the <code>classes</code> folder. You should now be able to run the Jalview application directly with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Jalview</code></pre></div>
-<p>You can also run with an automatic large memory setting (which will set the maximum memory heap of the Jalview JVM to 90% of your local physical memory) and docked icon setting (if possible in your OS) with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Launcher</code></pre></div>
+<pre><code class="language-bash">gradle compileJava
+</code></pre>
+<p>to compile the classes into the <code>classes</code> folder.
+You should now be able to run the Jalview application directly with</p>
+<pre><code class="language-bash">java -cp &quot;classes:resources:help:j11lib/*&quot; jalview.bin.Jalview
+</code></pre>
+<p>You can also run with an automatic large memory setting (which will set the maximum
+memory heap of the Jalview JVM to 90% of your local physical memory) and docked icon setting
+(if possible in your OS) with</p>
+<pre><code class="language-bash">java -cp &quot;classes:resources:help:j11lib/*&quot; jalview.bin.Launcher
+</code></pre>
 <blockquote>
-<p><em>You must use just &quot;<code>j11lib/*</code>&quot; and not &quot;<code>j11lib/*.jar</code>&quot; as this is a special Java classpath argument wildcard interpreted by <code>java</code>, <strong>not</strong> a shell expansion wildcard interpreted by the shell.</em></p>
+<p><em>You must use just &quot;<code>j11lib/*</code>&quot; and not &quot;<code>j11lib/*.jar</code>&quot; as this is a special Java
+classpath argument wildcard interpreted by <code>java</code>, <strong>not</strong> a shell expansion wildcard interpreted
+by the shell.</em></p>
 </blockquote>
-<p>Note that <code>jalview.bin.Launcher</code> is a simplified launcher class that re-launches <code>jalview.bin.Jalview</code> with the same JRE (<em>not</em> the same JVM instance), classpath and arguments, but with an automatically determined <code>-Xmx...</code> memory setting if one hasn't been provided.</p>
-<h3 id="jalview-in-a-jar-file">Jalview in a Jar File</h3>
+<p>Note that <code>jalview.bin.Launcher</code> is a simplified launcher class that re-launches <code>jalview.bin.Jalview</code>
+with the same JRE (<em>not</em> the same JVM instance), classpath and arguments, but with an automatically determined <code>-Xmx...</code>
+memory setting if one hasn't been provided.</p>
+<h3 id="jalview-in-a-jar-file"><a href="#jalview-in-a-jar-file" name="jalview-in-a-jar-file" class="anchor"><span class="octicon octicon-link"></span>Jalview in a Jar File</a></h3>
 <p>To package the <code>classes</code>, <code>resources</code>, and <code>help</code> into one jar, you can run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> jar</code></pre></div>
+<pre><code class="language-bash">gradle jar
+</code></pre>
 <p>which assembles the Jalview classes and resources into <code>dist/jalview.jar</code></p>
 <p>To run this, use</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;dist/jalview.jar:j11lib/*&quot;</span> jalview.bin.Jalview</code></pre></div>
-<h3 id="distributed-jar-files">Distributed Jar Files</h3>
-<p>To simplify this, all required <code>.jar</code> files can be assembled into the <code>dist</code> folder using</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> makeDist</code></pre></div>
+<pre><code class="language-bash">java -cp &quot;dist/jalview.jar:j11lib/*&quot; jalview.bin.Jalview
+</code></pre>
+<h3 id="distributed-jar-files"><a href="#distributed-jar-files" name="distributed-jar-files" class="anchor"><span class="octicon octicon-link"></span>Distributed Jar Files</a></h3>
+<p>To simplify this, all required <code>.jar</code> files can be assembled into the <code>dist</code> folder
+using</p>
+<pre><code class="language-bash">gradle makeDist
+</code></pre>
 <p>which puts all required jar files into <code>dist</code> so you can run with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;dist/*&quot;</span> jalview.bin.Jalview</code></pre></div>
-<h3 id="single-shadow-jar-file">Single <em>shadow</em> Jar File</h3>
-<p>The shadow jar file is a single <code>.jar</code> that contains all required classes and resources from <code>jalview.jar</code> and all of the supporting libraries in <code>j11lib/*.jar</code> merged into one <code>.jar</code> archive file. A default launching class (<code>MAIN-CLASS: jalview.bin.Launcher</code>) is specified in the <code>.jar</code> manifest file (<code>META/MANIFEST.MF</code>) so a start class doesn't need to be specified.</p>
+<pre><code class="language-bash">java -cp &quot;dist/*&quot; jalview.bin.Jalview
+</code></pre>
+<h3 id="single-shadow-jar-file"><a href="#single-shadow-jar-file" name="single-shadow-jar-file" class="anchor"><span class="octicon octicon-link"></span>Single <em>shadow</em> Jar File</a></h3>
+<p>The shadow jar file is a single <code>.jar</code> that contains all required classes and resources from <code>jalview.jar</code>
+and all of the supporting libraries in <code>j11lib/*.jar</code> merged into one <code>.jar</code> archive
+file.  A default launching class (<code>MAIN-CLASS: jalview.bin.Launcher</code>) is specified in the <code>.jar</code>
+manifest file (<code>META/MANIFEST.MF</code>) so a start class doesn't need to be specified.</p>
 <p>Build the shadow jar file in <code>build/lib/jalview-all-11.jar</code> with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> shadowJar</code></pre></div>
+<pre><code class="language-bash">gradle shadowJar
+</code></pre>
 <p>and run it with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -jar build/lib/jalview-all-11.jar</code></pre></div>
-<p>Because no arguments are required, most OSes will associate a <code>.jar</code> file with the <code>java</code> application (if this has been installed through the OS and not just a local unzip) as a <code>-jar</code> argument so you may find you can launch <code>jalview-all-11.jar</code> just by double-clicking on it)!</p>
+<pre><code class="language-bash">java -jar build/lib/jalview-all-11.jar
+</code></pre>
+<p>Because no arguments are required, most OSes will associate a <code>.jar</code> file with the
+<code>java</code> application (if this has been installed through the OS and not just a local
+unzip) as a <code>-jar</code> argument so you may find you can launch <code>jalview-all-11.jar</code>
+just by double-clicking on it)!</p>
 <blockquote>
-<p>The <code>shadowJar</code> task is not a requirement for any other task, so to build the shadow jar file you must specify the <code>shadowJar</code> task.</p>
+<p>The <code>shadowJar</code> task is not a requirement for any other task, so to build the shadow
+jar file you must specify the <code>shadowJar</code> task.</p>
 </blockquote>
 <blockquote>
-<p>The shadow jar file represents probably the simplest way to distribute the Jalview application to machines that already have a Java 11 installed, although without the many and compelling benefits of the <code>getdown</code> launcher.</p>
+<p>The shadow jar file represents probably the simplest way to distribute the Jalview application to machines that already have a Java 11 installed,
+although without the many and compelling benefits of the <code>getdown</code> launcher.</p>
 </blockquote>
-<h3 id="building-the-getdown-launcher">Building the <code>getdown</code> launcher</h3>
-<p>We have made significant customisations to the <code>getdown</code> launcher which you can find in <code>getdown/src/getdown</code>.</p>
+<h3 id="building-the-getdown-launcher"><a href="#building-the-getdown-launcher" name="building-the-getdown-launcher" class="anchor"><span class="octicon octicon-link"></span>Building the <code>getdown</code> launcher</a></h3>
+<p>We have made significant customisations to the <code>getdown</code> launcher which you can find
+in <code>getdown/src/getdown</code>.</p>
 <blockquote>
-<p>You don't need to build this afresh as the required <code>gradle-core.jar</code> and <code>gradle-launcher.jar</code> files are already distributed in <code>j11lib</code> and <code>getdown/lib</code> but if you want to, then you'll need a working Maven and also a Java 8 JDK. Ensure the Java 8 <code>javac</code> is forefront in your path and do</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="bu">cd</span> getdown/src/getdown
-<span class="ex">mvn</span> clean package -Dgetdown.host.whitelist=<span class="st">&quot;jalview.org,*.jalview.org&quot;</span></code></pre></div>
-<p>and you will find the required <code>.jar</code> files in <code>core/target/gradle-core-XXX.jar</code> and <code>launcher/target/gradle-launcher-XXX.jar</code>. The <code>gradle-core.jar</code> should then be copied to all three of the <code>j8lib</code>, <code>j11lib</code> and <code>getdown/lib</code> folders, whilst the <code>gradle-launcher.jar</code> only needs to be copied to <code>getdown/lib</code>.</p>
-<p>The <code>mvn</code> command should ideally include the <code>-Dgetdown.host.whitelist=*.jalview.org</code> setting. This, and the necessary file copying commands, can be found in <code>getdown/src/getdown/mvn_cmd</code>.</p>
+<p>You don't need to build this afresh as the required <code>gradle-core.jar</code>
+and <code>gradle-launcher.jar</code> files are already distributed in <code>j11lib</code> and <code>getdown/lib</code> but if you want to, then
+you'll need a working Maven and also a Java 8 JDK.  Ensure the Java 8 <code>javac</code> is forefront
+in your path and do</p>
+<pre><code class="language-bash">cd getdown/src/getdown
+mvn clean package -Dgetdown.host.whitelist=&quot;jalview.org,*.jalview.org&quot;
+</code></pre>
+<p>and you will find the required <code>.jar</code> files in <code>core/target/gradle-core-XXX.jar</code>
+and <code>launcher/target/gradle-launcher-XXX.jar</code>.  The <code>gradle-core.jar</code> should then be copied
+to all three of the <code>j8lib</code>, <code>j11lib</code> and <code>getdown/lib</code> folders, whilst the <code>gradle-launcher.jar</code> only
+needs to be copied to <code>getdown/lib</code>.</p>
+<p>The <code>mvn</code> command should ideally include the <code>-Dgetdown.host.whitelist=*.jalview.org</code> setting.
+This, and the necessary file copying commands, can be found in <code>getdown/src/getdown/mvn_cmd</code>.</p>
 </blockquote>
 <p>To assemble Jalview with <code>getdown</code> use the following gradle task:</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> getdown</code></pre></div>
-<p>This puts all the necessary files to launch Jalview with <code>getdown</code> into <code>getdown/website/11/</code>. This could be treated as the reference folder for <code>getdown</code>, which is where a getdown launcher will check to see if the Jalview application files it has are up to date, and download if they aren't or it simply doesn't have them.</p>
-<p>A minimal getdown-launcher can be found in <code>getdown/files/11/</code> which checks its up-to-date status with (the absolute path to) <code>getdown/website/11/</code>.</p>
+<pre><code class="language-bash">gradle getdown
+</code></pre>
+<p>This puts all the necessary files to launch Jalview with <code>getdown</code>
+into <code>getdown/website/11/</code>.  This could be treated as the reference folder
+for <code>getdown</code>, which is where a getdown launcher will check to see if the Jalview application
+files it has are up to date, and download if they aren't or it simply doesn't have
+them.</p>
+<p>A minimal getdown-launcher can be found in <code>getdown/files/11/</code> which checks its up-to-date
+status with (the absolute path to) <code>getdown/website/11/</code>.</p>
 <p>This can be launched with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview</code></pre></div>
+<pre><code class="language-bash">java -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview
+</code></pre>
 <blockquote>
-<p>We've already met the <code>-jar file.jar</code> arguments. The next argument is the working folder for getdown, and the final argument, &quot;<code>jalview</code>&quot;, is a getdown application id (only &quot;<code>jalview</code>&quot; is defined here).</p>
+<p>We've already met the <code>-jar file.jar</code> arguments.  The next argument is the working folder for
+getdown, and the final argument, &quot;<code>jalview</code>&quot;, is a getdown application id (only &quot;<code>jalview</code>&quot;
+is defined here).</p>
 </blockquote>
-<h3 id="running-tests">Running tests</h3>
+<h3 id="running-tests"><a href="#running-tests" name="running-tests" class="anchor"><span class="octicon octicon-link"></span>Running tests</a></h3>
 <p>There are substantial tests written for Jalview that use TestNG, which you can run with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test</code></pre></div>
-<p>These normally take around 5 - 10 minutes to complete and outputs its full results into the <code>tests/</code> folder. A summary of results should appear in your console.</p>
+<pre><code class="language-bash">gradle test
+</code></pre>
+<p>These normally take around 5 - 10 minutes to complete and outputs its full results into
+the <code>tests/</code> folder.  A summary of results should appear in your console.</p>
 <p>You can run different defined groups of tests with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test -PtestngGroups=Network</code></pre></div>
+<pre><code class="language-bash">gradle test -PtestngGroups=Network
+</code></pre>
 <p>Available groups include Functional (default), Network, External.</p>
-<h4 id="excluding-some-tests">Excluding some tests</h4>
+<h4 id="excluding-some-tests"><a href="#excluding-some-tests" name="excluding-some-tests" class="anchor"><span class="octicon octicon-link"></span>Excluding some tests</a></h4>
 <p>Some of Jalview's Functional tests don't pass reliably in all environments. We tag these tests with a group like 'Not-bamboo' to mark them for exclusion when we run tests as part of continuous integration.</p>
 <p>To exclude one or more groups of tests, add them as a comma separated list in testngExcludedGroups.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test -PtestngExcludedGroups=Not-bamboo</code></pre></div>
-<h3 id="installer-packaging-with-install4j">Installer packaging with <em>install4j</em></h3>
-<p>Jalview is currently using <em>install4j</em> <a href="https://www.ej-technologies.com/products/install4j/overview.html" class="uri">https://www.ej-technologies.com/products/install4j/overview.html</a> as its installer packaging tool.</p>
-<p>If you have a licensed installation of <em>install4j</em> you can build Jalview installers by running</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> installers</code></pre></div>
-<p>though you may need to fiddle with the <code>install4j</code> and <code>copyInstall4jTemplate</code> tasks in <code>build.gradle</code> file to point to your installation of <em>install4j</em> and also to bundled JREs if you want to bundle those into the installers.</p>
-<p>If you want more details, get in touch on our development mailing list <a href="mailto:jalview-dev@jalview.org">jalview-dev@jalview.org</a>. Sign up at <a href="http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev" class="uri">http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev</a>.</p>
-<h2 id="gradle-properties">Gradle properties</h2>
-<p>There are a lot of properties configured in <code>gradle.properties</code> which we strongly recommend being left as they are unless you have a specific problem with the build process.</p>
-<p>There are a few gradle properties you might want to set on the command line with the <code>-P</code> flag when building a version of Jalview with specific requirements:</p>
-<h4 id="java_version"><code>JAVA_VERSION</code></h4>
-<p>This changes the <em>target</em> java bytecode version &gt; NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build Jalview for any byte-code target version.</p>
+<pre><code class="language-bash">gradle test -PtestngExcludedGroups=Not-bamboo
+</code></pre>
+<h3 id="installer-packaging-with-install4j"><a href="#installer-packaging-with-install4j" name="installer-packaging-with-install4j" class="anchor"><span class="octicon octicon-link"></span>Installer packaging with <em>install4j</em></a></h3>
+<p>Jalview is currently using <em>install4j</em> <a href="https://www.ej-technologies.com/products/install4j/overview.html">https://www.ej-technologies.com/products/install4j/overview.html</a>
+as its installer packaging tool.</p>
+<p>If you have a licensed installation of <em>install4j</em> you can build Jalview installers
+by running</p>
+<pre><code class="language-bash">gradle installers
+</code></pre>
+<p>though you may need to fiddle with the <code>install4j</code> and <code>copyInstall4jTemplate</code> tasks
+in <code>build.gradle</code> file to point to your installation of <em>install4j</em> and also to bundled
+JREs if you want to bundle those into the installers.</p>
+<p>If you want more details, get in touch on our development mailing list <a href="mailto:jalview-dev@jalview.org">jalview-dev@jalview.org</a>.
+Sign up at <a href="http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev">http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev</a>.</p>
+<h2 id="gradle-properties"><a href="#gradle-properties" name="gradle-properties" class="anchor"><span class="octicon octicon-link"></span>Gradle properties</a></h2>
+<p>There are a lot of properties configured in <code>gradle.properties</code> which we strongly recommend
+being left as they are unless you have a specific problem with the build process.</p>
+<p>There are a few gradle properties you might want to set on the command line with the
+<code>-P</code> flag when building a version of Jalview with specific requirements:</p>
+<h4 id="java-version"><a href="#java-version" name="java-version" class="anchor"><span class="octicon octicon-link"></span><code>JAVA_VERSION</code></a></h4>
+<p>This changes the <em>target</em> java bytecode version</p>
+<blockquote>
+<p>NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build
+Jalview for any byte-code target version.</p>
+</blockquote>
 <p>Valid values are <code>11</code> and <code>1.8</code>.</p>
 <p>e.g.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> shadowJar -PJAVA_VERSION=1.8</code></pre></div>
+<pre><code class="language-bash">gradle shadowJar -PJAVA_VERSION=1.8
+</code></pre>
 <p>When using <code>-PJAVA_VERSION=1.8</code> the libraries from <code>j8lib</code> (instead of <code>j11lib</code>) will be used in the compile<br />
-and runtime classpath and also used in the <code>makeDist</code> build step. Where a Java version of <code>11</code> is used in folder and file names, it will instead use <code>1.8</code>. Also if you are building installer packages with <em>install4j</em> the package builder will look for JRE 1.8 bundles to package in the installers.</p>
+and runtime classpath and also used in the <code>makeDist</code> build step.  Where a Java version of <code>11</code> is used in folder and file names, it will
+instead use <code>1.8</code>.  Also if you are building installer packages with <em>install4j</em> the
+package builder will look for JRE 1.8 bundles to package in the installers.</p>
 <blockquote>
-<p>Note that continued development of Jalview will assume a Java 11+ runtime environment, the 2.11.0 release will run under a Java 1.8 JRE with a few minor features disabled.</p>
+<p>Note that continued development of Jalview will assume a Java 11+ runtime environment,
+the 2.11.0 release will run under a Java 1.8 JRE with a few minor features disabled.</p>
 </blockquote>
-<h4 id="channel"><code>CHANNEL</code></h4>
-<p>This changes the <code>appbase</code> setting in <code>getdown.txt</code> (<code>appbase</code> is where the getdown launcher looks to see if there's an updated file) to point to a particular Jalview channel or some other appropriate place to look for required files. If the selected channel type requires the getdown <code>appbase</code> to be a local directory on the filesystem (instead of a website URL) then a modified version of the <code>getdown-launcher.jar</code> will be used to allow this. The two versions of the <code>getdown-launcher.jar</code> can be found in <code>getdown/lib</code>. Some other variables used in the build process might also be set differently depending on the value of <code>CHANNEL</code> to allow smooth operation of getdown in the given context.</p>
-<p>There are several values of <code>CHANNEL</code> that can be chosen, with a default of <code>LOCAL</code>. Here's what they're for and what they do:</p>
+<h4 id="channel"><a href="#channel" name="channel" class="anchor"><span class="octicon octicon-link"></span><code>CHANNEL</code></a></h4>
+<p>This changes the <code>appbase</code> setting in <code>getdown.txt</code> (<code>appbase</code> is where the getdown launcher
+looks to see if there's an updated file) to point to a particular Jalview channel or some other appropriate
+place to look for required files.  If the selected channel type requires the getdown <code>appbase</code> to be a local
+directory on the filesystem (instead of a website URL) then a modified version of the <code>getdown-launcher.jar</code> will
+be used to allow this.  The two versions of the <code>getdown-launcher.jar</code> can be found in <code>getdown/lib</code>.
+Some other variables used in the build process might also be set differently depending on the value of <code>CHANNEL</code>
+to allow smooth operation of getdown in the given context.</p>
+<p>There are several values of <code>CHANNEL</code> that can be chosen, with a default of <code>LOCAL</code>.  Here's what they're for and what they do:</p>
 <ul>
-<li><code>LOCAL</code>: This is for running the compiled application from the development directory. It will set
+<li><code>LOCAL</code>: This is for running the compiled application from the development directory.
+It will set
 <ul>
-<li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/files/JAVA_VERSION</code> (e.g. <code>file://home/user/git/jalview/getdown/files/11</code>)</li>
+<li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/files/JAVA_VERSION</code>
+(e.g. <code>file://home/user/git/jalview/getdown/files/11</code>)</li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>BUILD</code>: This is for creating an appbase channel on the build server by an automatic or manually started build. It will set
+</ul>
+</li>
+<li><code>BUILD</code>: This is for creating an appbase channel on the build server by an automatic or manually started build.
+It will set
 <ul>
-<li><code>appbase</code> as <code>https://builds.jalview.org/browse/${bamboo_planKey}/latest/artifact/shared/getdown-channel/JAVA_VERSION</code> Note that bamboo_planKey should be set by the build plan with <code>-Pbamboo_planKey=${bamboo.planKey}</code></li>
+<li><code>appbase</code> as <code>https://builds.jalview.org/browse/${bamboo_planKey}/latest/artifact/shared/getdown-channel/JAVA_VERSION</code>
+Note that bamboo_planKey should be set by the build plan with <code>-Pbamboo_planKey=${bamboo.planKey}</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>DEVELOP</code>: This is for creating a <code>develop</code> appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server. It will set
+</ul>
+</li>
+<li><code>DEVELOP</code>: This is for creating a <code>develop</code> appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server.
+It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/develop/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>SCRATCH-NAME</code>: This is for creating a temporary scratch appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. The value of <code>NAME</code> can be any &quot;word-character&quot; [A-Za-z0-9_] It will set
+</ul>
+</li>
+<li><code>SCRATCH-NAME</code>: This is for creating a temporary scratch appbase channel on the main web server.  This won't become live until the actual getdown artefact is synced to the web server.  This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels.  The value of <code>NAME</code> can be any &quot;word-character&quot; [A-Za-z0-9_]
+It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/SCRATCH-NAME/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>TEST-LOCAL</code>: Like <code>SCRATCH</code> but with a specific <code>test-local</code> channel name and a local filesystem appbase. This is meant for testing an over-the-air update on the local filesystem. An extra property <code>LOCALDIR</code> must be given (e.g. <code>-PLOCALDIR=/home/user/tmp/test</code>) It will set
+</ul>
+</li>
+<li><code>TEST-LOCAL</code>:  Like <code>SCRATCH</code> but with a specific <code>test-local</code> channel name and a local filesystem appbase.  This is meant for testing an over-the-air update on the local filesystem.  An extra property <code>LOCALDIR</code> must be given (e.g. <code>-PLOCALDIR=/home/user/tmp/test</code>)
+It will set
 <ul>
 <li><code>appbase</code> as <code>file://${LOCALDIR}</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>TEST-RELEASE</code>: Like <code>SCRATCH</code> but with a specific <code>test-release</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. It will set
+</ul>
+</li>
+<li><code>TEST-RELEASE</code>:  Like <code>SCRATCH</code> but with a specific <code>test-release</code> channel name.  This won't become live until the actual getdown artefact is synced to the web server.  This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels.
+It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/test-release/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>RELEASE</code>: This is for an actual release build, and will use an appbase on the main web server with the main <code>release</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. It will set
+</ul>
+</li>
+<li><code>RELEASE</code>:  This is for an actual release build, and will use an appbase on the main web server with the main <code>release</code> channel name.  This won't become live until the actual getdown artefact is synced to the web server.
+It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/release/JAVA_VERSION</code></li>
 <li>application subdir as <code>release</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>ARCHIVE</code>: This is a helper to create a channel for a specific release version, and will use an appbase on the main web server with a specific <code>archive/JALVIEW_VERSION</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set
+</ul>
+</li>
+<li><code>ARCHIVE</code>:  This is a helper to create a channel for a specific release version, and will use an appbase on the main web server with a specific <code>archive/JALVIEW_VERSION</code> channel name.  This won't become live until the actual getdown artefact is synced to the web server.
+You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files.  This should create a getdown structure and digest with the older jar files.
+It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/archive/JALVIEW_VERSION/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
-</ul></li>
-<li><code>ARCHIVELOCAL</code>: Like <code>ARCHIVE</code> but with a local filesystem appbase for local testing. You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set
+</ul>
+</li>
+<li><code>ARCHIVELOCAL</code>:  Like <code>ARCHIVE</code> but with a local filesystem appbase for local testing.
+You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files.  This should create a getdown structure and digest with the older jar files.
+It will set
 <ul>
 <li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/website/JAVA_VERSION</code> (where the old jars will have been copied and digested)</li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
-</ul></li>
+</ul>
+</li>
 </ul>
 <p>e.g.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> getdown -PCHANNEL=SCRATCH-my_test_version</code></pre></div>
-<h4 id="install4jmediatypes"><code>install4jMediaTypes</code></h4>
-<p>If you are building <em>install4j</em> installers (requires <em>install4j</em> to be installed) then this property specifies a comma-separated list of media types (i.e. platform specific installers) <em>install4j</em> should actually build.</p>
-<p>Currently the valid values are <code>linuxDeb</code>, <code>linuxRPM</code>, <code>macosArchive</code>, <code>unixArchive</code>, <code>unixInstaller</code>, <code>windows</code></p>
+<pre><code class="language-bash">gradle getdown -PCHANNEL=SCRATCH-my_test_version
+</code></pre>
+<h4 id="jalview-version-and-the-release-file"><a href="#jalview-version-and-the-release-file" name="jalview-version-and-the-release-file" class="anchor"><span class="octicon octicon-link"></span>JALVIEW_VERSION and the RELEASE file</a></h4>
+<p>Any Jalview build will include the value of JALVIEW_VERSION in various places, including the 'About' and Jalview Desktop window title, and in filenames for the stand-alone executable jar. You can specify a custom version for a build via the JALVIEW_VERSION property, but for most situations, JALVIEW_VERSION will be automatically configured according to the value of the CHANNEL property, using the <code>jalview.version</code> property specified in the RELEASE file:</p>
+<ul>
+<li><code>CHANNEL=RELEASE</code> will set version to jalview.version</li>
+<li><code>CHANNEL=TEST or DEVELOP</code> will append '-test' or '-develop' to jalview.version</li>
+</ul>
+<p>It is also possible to specify a custom location for the RELEASE file via an optional JALVIEW_RELEASE_FILE property.</p>
+<h4 id="install4jmediatypes"><a href="#install4jmediatypes" name="install4jmediatypes" class="anchor"><span class="octicon octicon-link"></span><code>install4jMediaTypes</code></a></h4>
+<p>If you are building <em>install4j</em> installers (requires <em>install4j</em> to be installed) then this property specifies a comma-separated
+list of media types (i.e. platform specific installers) <em>install4j</em> should actually build.</p>
+<p>Currently the valid values are
+<code>linuxDeb</code>,
+<code>linuxRPM</code>,
+<code>macosArchive</code>,
+<code>unixArchive</code>,
+<code>unixInstaller</code>,
+<code>windows</code></p>
 <p>The default value is all of them.</p>
 <p>e.g.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive</code></pre></div>
+<pre><code class="language-bash">gradle installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive
+</code></pre>
 <p>To get an up-to-date list of possible values, you can run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">perl</span> -n -e <span class="st">&#39;m/^\s*&lt;(\w+)[^&gt;]*\bmediaFileName=/ &amp;&amp; print &quot;$1\n&quot;;&#39;</span> utils/install4j/install4j_template.install4j  <span class="kw">|</span> <span class="fu">sort</span> -u</code></pre></div>
+<pre><code class="language-bash">perl -n -e 'm/^\s*&lt;(\w+)[^&gt;]*\bmediaFileName=/ &amp;&amp; print &quot;$1\n&quot;;' utils/install4j/install4j_template.install4j  | sort -u
+</code></pre>
 <p>in the <code>jalview</code> root folder.</p>
-<h2 id="enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</h2>
+<h2 id="enabling-code-coverage-with-openclover"><a href="#enabling-code-coverage-with-openclover" name="enabling-code-coverage-with-openclover" class="anchor"><span class="octicon octicon-link"></span>Enabling Code Coverage with OpenClover</a></h2>
 <p>Bytecode instrumentation tasks are enabled by specifying 'true' (or just a non-whitespace non-numeric word) in the 'clover' property. This adds the 'openclover' plugin to the build script's classpath, making it possible to track code execution during test which can be viewed as an HTML report published at build/reports/clover/index.html.</p>
 <p><code>gradle -Pclover=true test cloverReport</code></p>
-<h4 id="troubleshooting-report-generation">Troubleshooting report generation</h4>
+<h4 id="troubleshooting-report-generation"><a href="#troubleshooting-report-generation" name="troubleshooting-report-generation" class="anchor"><span class="octicon octicon-link"></span>Troubleshooting report generation</a></h4>
 <p>The build forks a new JVM process to run the clover report generation tools (both XML and HTML reports are generated by default). The following properties can be used to specify additional options or adjust JVM memory settings. Default values for these options are:</p>
-<h5 id="jvm-memory-settings---increase-if-out-of-memory-errors-are-reported">JVM Memory settings - increase if out of memory errors are reported</h5>
+<h5 id="jvm-memory-settings---increase-if-out-of-memory-errors-are-reported"><a href="#jvm-memory-settings---increase-if-out-of-memory-errors-are-reported" name="jvm-memory-settings---increase-if-out-of-memory-errors-are-reported" class="anchor"><span class="octicon octicon-link"></span>JVM Memory settings - increase if out of memory errors are reported</a></h5>
 <p><code>cloverReportJVMHeap = 2g</code></p>
-<h5 id="dfile.encodingutf-8-is-an-essential-parameters-for-report-generation.-add-additional-ones-separated-by-a-space.">-Dfile.encoding=UTF-8 is an essential parameters for report generation. Add additional ones separated by a space.</h5>
+<h5 id="-dfileencodingutf-8-is-an-essential-parameters-for-report-generation-add-additional-ones-separated-by-a-space"><a href="#-dfileencodingutf-8-is-an-essential-parameters-for-report-generation-add-additional-ones-separated-by-a-space" name="-dfileencodingutf-8-is-an-essential-parameters-for-report-generation-add-additional-ones-separated-by-a-space" class="anchor"><span class="octicon octicon-link"></span>-Dfile.encoding=UTF-8 is an essential parameters for report generation. Add additional ones separated by a space.</a></h5>
 <p><code>cloverReportJVMArgs = -Dfile.encoding=UTF-8</code></p>
-<h5 id="add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database">Add -v to debug velocity html generation errors, or -d to track more detailed issues with the coverage database</h5>
+<h5 id="add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database"><a href="#add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database" name="add--v-to-debug-velocity-html-generation-errors-or--d-to-track-more-detailed-issues-with-the-coverage-database" class="anchor"><span class="octicon octicon-link"></span>Add -v to debug velocity html generation errors, or -d to track more detailed issues with the coverage database</a></h5>
 <p><code>cloverReportHTMLOptions =</code></p>
-<h5 id="v-for-verbose--d-for-debug-level-messages-as-above">-v for verbose, -d for debug level messages (as above)</h5>
+<h5 id="-v-for-verbose--d-for-debug-level-messages-as-above"><a href="#-v-for-verbose--d-for-debug-level-messages-as-above" name="-v-for-verbose--d-for-debug-level-messages-as-above" class="anchor"><span class="octicon octicon-link"></span>-v for verbose, -d for debug level messages (as above)</a></h5>
 <p><code>cloverReportXMLOptions =</code></p>
 <p><em>Note</em> do not forget to include the -Dfile.encoding=UTF-8 option: this is essential for some platforms in order for Clover to correctly parse some Jalview source files that contain characters that are UTF-8 encoded.</p>
-<h2 id="setting-up-in-eclipse-ide">Setting up in Eclipse IDE</h2>
-<h3 id="installing-eclipse-ide">Installing Eclipse IDE</h3>
-<p>We develop in Eclipse, and support settings to develop and save Jalview source code in our preferred style. We also support running the Jalview application, debugging and running tests with TestNG from within Eclipse.</p>
-<p>To get Jalview set up as a project in Eclipse, we recommend using at least the 2019-12 version of Eclipse IDE for Java Developers which you can download from the Eclipse website: <a href="https://www.eclipse.org/downloads/" class="uri">https://www.eclipse.org/downloads/</a>. Since Eclipse 2020-03 you are encouraged to use the Eclipse Installer (see the Eclipse Downloads page). In the installer, when given a choice of packages for Eclipse you should choose the &quot;Eclipse IDE for Enterprise Java Developers&quot; package.</p>
-<div class="figure">
-<img src="./images/eclipse_installer.png" title="Eclipse Installer screenshot" />
-
-</div>
+<h2 id="setting-up-in-eclipse-ide"><a href="#setting-up-in-eclipse-ide" name="setting-up-in-eclipse-ide" class="anchor"><span class="octicon octicon-link"></span>Setting up in Eclipse IDE</a></h2>
+<h3 id="installing-eclipse-ide"><a href="#installing-eclipse-ide" name="installing-eclipse-ide" class="anchor"><span class="octicon octicon-link"></span>Installing Eclipse IDE</a></h3>
+<p>We develop in Eclipse, and support settings to develop and save Jalview source code
+in our preferred style.  We also support running the Jalview application, debugging
+and running tests with TestNG from within Eclipse.</p>
+<p>To get Jalview set up as a project in Eclipse, we recommend using at least the 2019-12
+version of Eclipse IDE for Java Developers which you can download from the Eclipse
+website: <a href="https://www.eclipse.org/downloads/">https://www.eclipse.org/downloads/</a>.  Since Eclipse 2020-03 you are encouraged to use the Eclipse Installer (see the Eclipse Downloads page).
+In the installer, when given a choice of packages for Eclipse you should choose the &quot;Eclipse IDE for Enterprise Java Developers&quot; package.</p>
+<p><img src="./images/eclipse_installer.png" alt="" title="Eclipse Installer screenshot" /></p>
 <p>Once Eclipse is installed, we also recommend installing several plugins from the Eclipse Marketplace.</p>
 <p>Some of these should already be installed with the Enterprise Java Developer package:</p>
-<ol style="list-style-type: decimal">
+<ol>
 <li>Buildship Gradle Integration 3.0 (or greater)</li>
 <li>EclEmma Java Code Coverage</li>
 <li>Egit - Git Integration for Eclipse</li>
 </ol>
 <p>To install the others, launch Eclipse, and go to Help-&gt;Eclipse Marketplace...</p>
 <p>Search for and install:</p>
-<ol style="list-style-type: decimal">
+<ol>
 <li>Groovy Development Tools 3.4.0 (or greater)</li>
 <li>Checkstyle Plug-in (optional)</li>
 <li>TestNG for Eclipse (optional -- only needed if you want to run tests from Eclipse)</li>
 </ol>
 <blockquote>
-<p>At time of writing, TestNG for Eclipse does not show up in the Eclipse Marketplace as the latest released version does not install in Eclipse 2019-03. However, you can install a working release of TestNG for Eclipse by going to</p>
+<p>At time of writing, TestNG for Eclipse does not show up in the Eclipse Marketplace
+as the latest released version does not install in Eclipse 2019-03.
+However, you can install a working release of TestNG for Eclipse by going to</p>
 <p>Help-&gt;Install New Software...</p>
 <p>and entering</p>
 <p><code>TestNG Release - https://dl.bintray.com/testng-team/testng-eclipse-release</code></p>
 <p>into the <em>Work with</em> box and click on the <em>Add...</em> button.</p>
-<p>Eclipse might pause for a bit with the word <em>Pending</em> in the table below at this point, but it will eventually list TestNG with a selection box under the <em>Name</em> column.</p>
-<p>Select <em>TestNG</em> and carry on through the install process to install the TestNG plugin.</p>
+<p>Eclipse might pause for a bit with the word <em>Pending</em> in the table below at this point, but it will eventually list TestNG with
+a selection box under the <em>Name</em> column.</p>
+<p>Select <em>TestNG</em> and carry on through the
+install process to install the TestNG plugin.</p>
 </blockquote>
 <p>After installing the plugins, check that Java 11 is set up in Eclipse as the default JRE (see section <a href="#java-11-compliant-jdk">Java 11 compliant JDK</a>).</p>
-<p>To do this go to Preferences (Eclipse-&gt;Preferences in macOS, File-&gt;Preferences on Windows or Window-&gt;Preferences on Linux) and find</p>
+<p>To do this go to Preferences (Eclipse-&gt;Preferences in macOS, File-&gt;Preferences
+on Windows or Window-&gt;Preferences on Linux) and find</p>
 <p>Java -&gt; Installed JREs</p>
 <p>If your Java 11 installation is not listed, click on</p>
 <p><em>Add</em> -&gt; Standard VM -&gt; <em>Next</em></p>
-<p>and enter the JRE home. You can browse to where it is installed. Give it a name (like &quot;AdoptOpenJDK 11&quot;). Select this JDK as the default JRE and click on <em>Apply and Close</em>.</p>
+<p>and enter the JRE home.  You can browse to where it is installed. Give it a name (like &quot;AdoptOpenJDK 11&quot;).  Select this JDK
+as the default JRE and click on <em>Apply and Close</em>.</p>
 <p>You can now import Jalview.</p>
-<h3 id="importing-jalview-as-an-eclipse-project">Importing Jalview as an Eclipse project</h3>
-<h4 id="importing-an-already-downloaded-git-repo">Importing an already downloaded git repo</h4>
+<h3 id="importing-jalview-as-an-eclipse-project"><a href="#importing-jalview-as-an-eclipse-project" name="importing-jalview-as-an-eclipse-project" class="anchor"><span class="octicon octicon-link"></span>Importing Jalview as an Eclipse project</a></h3>
+<h4 id="importing-an-already-downloaded-git-repo"><a href="#importing-an-already-downloaded-git-repo" name="importing-an-already-downloaded-git-repo" class="anchor"><span class="octicon octicon-link"></span>Importing an already downloaded git repo</a></h4>
 <p>If you have already downloaded Jalview using <code>git clone</code> then you can import this folder into Eclipse directly.</p>
-<p>It is important to import Jalview as a Gradle project (not as a Java project), so go to</p>
+<p>It is important to import
+Jalview as a Gradle project (not as a Java project), so go to</p>
 <p>File-&gt;Import...</p>
 <p>find and select</p>
 <p>Gradle-&gt;Existing Gradle Project</p>
 <p>and then click on the <em>Next</em> button.</p>
-<p>In the following options, it is the <strong>Project Root Directory</strong> you should set to be the <code>jalview</code> folder that git downloaded. Then you can click on the <em>Finish</em> button.</p>
-<h4 id="using-eclipse-ide-to-download-the-git-repo">Using Eclipse IDE to download the git repo</h4>
-<p>If you don't have git as a command line tool or would prefer to work entirely within Eclipse IDE then Eclipse's eGit plugin can set up a git repo of the jalview source. Go to</p>
+<p>In the following options, it is the <strong>Project Root Directory</strong> you should set to be the
+<code>jalview</code> folder that git downloaded.  Then you can click on the <em>Finish</em> button.</p>
+<h4 id="using-eclipse-ide-to-download-the-git-repo"><a href="#using-eclipse-ide-to-download-the-git-repo" name="using-eclipse-ide-to-download-the-git-repo" class="anchor"><span class="octicon octicon-link"></span>Using Eclipse IDE to download the git repo</a></h4>
+<p>If you don't have git as a command line tool or would prefer to work entirely within Eclipse IDE then
+Eclipse's eGit plugin can set up a git repo of the jalview source.  Go to</p>
 <p>File-&gt;Import...</p>
 <p>Find and select</p>
 <p>Git-&gt;Projects from Git</p>
 <p>and then click on the <em>Next</em> button.</p>
 <p>Then select Clone URI and click on <em>Next</em>.</p>
-<p>In the next window (Source Git Repository) you should put the <code>git clone</code> URL in the text box labelled <code>URI</code>. If you have a Jalview developer account (with a username and password for the Jalview git repository) then you should enter <code>https://source.jalview.org/git/jalview.git</code>. If you do not have a Jalview developer account then you should enter <code>http://source.jalview.org/git/jalview.git</code>. You will not be able to push any of your changes back to the Jalview git repository. However you can still pull all branches of the Jalview source code to your computer and develop the code there. &gt; You can sign up for a Jalview developer account at <a href="https://source.jalview.org/crucible/" class="uri">https://source.jalview.org/crucible/</a></p>
-<p>If you have a Jalview developer account, enter the username and password and decide if you want to use Eclipse's secure storage. If you don't have an account you can leave the Authentication section blank.</p>
-<div class="figure">
-<img src="./images/eclipse_egit_connection.png" alt="Eclipse eGit connection configuration" />
-<p class="caption">Eclipse eGit connection configuration</p>
-</div>
+<p>In the next window (Source Git Repository) you should put the <code>git clone</code> URL in the text box labelled <code>URI</code>.  If you have a Jalview developer account (with a username and password for the Jalview git repository) then you should enter
+<code>https://source.jalview.org/git/jalview.git</code>.
+If you do not have a Jalview developer account then you should enter
+<code>http://source.jalview.org/git/jalview.git</code>.
+You will not be able to push any of your changes back to the Jalview git repository. However you can still pull all branches of the Jalview source code to your computer and develop the code there.</p>
+<blockquote>
+<p>You can sign up for a Jalview developer account at <a href="https://source.jalview.org/crucible/">https://source.jalview.org/crucible/</a></p>
+</blockquote>
+<p>If you have a Jalview developer account, enter the username and password and decide if you want to use Eclipse's secure storage.  If you don't have an account you can leave the Authentication section blank.</p>
+<p><img src="./images/eclipse_egit_connection.png" alt="Eclipse eGit connection configuration" /></p>
 <p>Click on the <em>Next</em> button.</p>
-<p>The next window (Branch Selection) gives a list of the many Jalview branches, which by default will be all checked. You probably only want to download one branch (you can always download others at a later time). This is likely to be the <code>develop</code> branch so you can click on the <em>Deselect All</em> button, find the <code>develop</code> branch (the filter text helps), select that, and then click on the <em>Next</em> button.</p>
-<p>Choose a directory to your copy of the git repo in, and leave the other options as they are and click on the <em>Next</em> button. The next stage may take a minute or two as it checks out the selected branch(es) from the Jalview git repository.</p>
-<p>When it has finished it is important to select <strong>Import as general project</strong> and then click on <em>Next</em>. &gt; Ideally there would be an <em>Import as gradle project</em> here but there isn't -- we'll sort that out later.</p>
-<div class="figure">
-<img src="./images/eclipse_egit_import.png" alt="Eclipse eGit import choice" />
-<p class="caption">Eclipse eGit import choice</p>
-</div>
+<p>The next window (Branch Selection) gives a list of the many Jalview branches, which by default will be all checked.  You probably only want to download one branch (you can always download others at a later time).  This is likely to be the <code>develop</code> branch so you can click on the <em>Deselect All</em> button, find the <code>develop</code> branch (the filter text helps), select that, and then click on the <em>Next</em> button.</p>
+<p>Choose a directory to your copy of the git repo in, and leave the other options as they are and click on the <em>Next</em> button.  The next stage may take a minute or two as it checks out the selected branch(es) from the Jalview git repository.</p>
+<p>When it has finished it is important to select <strong>Import as general project</strong> and then click on <em>Next</em>.</p>
+<blockquote>
+<p>Ideally there would be an <em>Import as gradle project</em> here but there isn't -- we'll sort that out later.</p>
+</blockquote>
+<p><img src="./images/eclipse_egit_import.png" alt="Eclipse eGit import choice" /></p>
 <p>Click on the <em>Next</em> button.</p>
-<p>You can change the project name here. By default it will show as <strong>jalview</strong> which is fine unless you have another instance of the a Jalview project also called jalview, in which case you could change this project's name now to avoid a conflict within Eclipse.</p>
+<p>You can change the project name here.  By default it will show as <strong>jalview</strong> which is fine unless you have another instance of the a Jalview project also called jalview, in which case you could change this project's name now to avoid a conflict within Eclipse.</p>
 <p>Click on <em>Finish</em>!</p>
 <p>However, we haven't finished...</p>
-<p>You should now see, and be able to expand, the jalview project in the Project Explorer. We need to tell eclipse that this is a Gradle project, which will then allow the Eclipse Buildship plugin to automatically configure almost everything else!</p>
+<p>You should now see, and be able to expand, the jalview project in the Project Explorer.  We need to tell eclipse that this is a Gradle project, which will then allow the Eclipse Buildship plugin to automatically configure almost everything else!</p>
 <p>Right click on the project name (jalview) in the Project Explorer and find Configure towards the bottom of this long context menu, then choose Add Gradle Nature.</p>
-<div class="figure">
-<img src="./images/eclipse_add_gradle_nature.png" alt="Eclipse Add Gradle Nature" />
-<p class="caption">Eclipse Add Gradle Nature</p>
-</div>
+<p><img src="./images/eclipse_add_gradle_nature.png" alt="Eclipse Add Gradle Nature" /></p>
 <p>The project should now reconfigure itself using the <code>build.gradle</code> file to dynamically set various aspects of the project including classpath.</p>
-<h4 id="additional-views">Additional views</h4>
-<p>Some views that are automatically added when Importing a Gradle Project are not added when simply Adding a Gradle Nature, but we can add these manually by clicking on Window-&gt;Show View-&gt;Console and Window-&gt;Show View-&gt;Other... Filter with the word &quot;gradle&quot; and choose both <strong>Gradle Executions</strong> and <strong>Gradle Tasks</strong> and then click on the <em>Open</em> button.</p>
-<p>Okay, ready to code! Use of Eclipse is beyond the scope of this document, but you can find more information about developing jalview and our developer workflow in the google doc <a href="https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing" class="uri">https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing</a></p>
+<h4 id="additional-views"><a href="#additional-views" name="additional-views" class="anchor"><span class="octicon octicon-link"></span>Additional views</a></h4>
+<p>Some views that are automatically added when Importing a Gradle Project are not added when simply Adding a Gradle Nature, but we can add these manually by clicking on
+Window-&gt;Show View-&gt;Console
+and
+Window-&gt;Show View-&gt;Other...
+Filter with the word &quot;gradle&quot; and choose both <strong>Gradle Executions</strong> and <strong>Gradle Tasks</strong> and then click on the <em>Open</em> button.</p>
+<p>Okay, ready to code!  Use of Eclipse is beyond the scope of this document, but you can find more information about developing jalview and our developer workflow in the google doc <a href="https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing">https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing</a></p>
 <hr />
 <p><a href="mailto:help@jalview.org">Jalview Development Team</a></p>
-</body>
+
+  </body>
 </html>
index ad19979..06e796d 100644 (file)
@@ -563,18 +563,18 @@ Search for and install:
 as the latest released version does not install in Eclipse 2019-03.
 However, you can install a working release of TestNG for Eclipse by going to
 >
-Help->Install New Software...
+> Help->Install New Software...
 >
-and entering
+> and entering
 >
-`TestNG Release - https://dl.bintray.com/testng-team/testng-eclipse-release`
+> `TestNG Release - https://dl.bintray.com/testng-team/testng-eclipse-release`
 >
-into the *Work with* box and click on the *Add...* button.
+> into the *Work with* box and click on the *Add...* button.
 >
-Eclipse might pause for a bit with the word *Pending* in the table below at this point, but it will eventually list TestNG with 
+> Eclipse might pause for a bit with the word *Pending* in the table below at this point, but it will eventually list TestNG with 
 a selection box under the *Name* column.
 >
-Select *TestNG* and carry on through the 
+> Select *TestNG* and carry on through the 
 install process to install the TestNG plugin.
 
 After installing the plugins, check that Java 11 is set up in Eclipse as the default JRE (see section [Java 11 compliant JDK](#java-11-compliant-jdk)).
diff --git a/doc/releaseprocess.html b/doc/releaseprocess.html
new file mode 100644 (file)
index 0000000..7205420
--- /dev/null
@@ -0,0 +1,692 @@
+<html>
+<!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>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta http-equiv="Content-Style-Type" content="text/css" />
+    <meta name="generator" content="flexmark" />
+  <title>Making a Jalview release</title>
+    <style type="text/css">code{white-space: pre;}</style>
+<style>
+@font-face {
+  font-family: octicons-link;
+  src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff');
+}
+
+body {
+  -webkit-text-size-adjust: 100%;
+  text-size-adjust: 100%;
+  color: #333;
+  font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+  font-size: 16px;
+  line-height: 1.6;
+  word-wrap: break-word;
+  width: 728px;
+  max-width: 99%;
+  box-sizing: border-box;
+  padding: 30px 30px 8rem 30px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+body a {
+  background-color: transparent;
+}
+
+body a:active,
+body a:hover {
+  outline: 0;
+}
+
+body strong {
+  font-weight: bold;
+}
+
+body h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+body img {
+  border: 0;
+}
+
+body hr {
+  box-sizing: content-box;
+  height: 0;
+}
+
+body pre {
+  overflow: auto;
+}
+
+body code,
+body kbd,
+body pre {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+
+body input {
+  color: inherit;
+  font: inherit;
+  margin: 0;
+}
+
+body html input[disabled] {
+  cursor: default;
+}
+
+body input {
+  line-height: normal;
+}
+
+body input[type="checkbox"] {
+  box-sizing: border-box;
+  padding: 0;
+}
+
+body table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+body td,
+body th {
+  padding: 0;
+}
+
+body * {
+  box-sizing: border-box;
+}
+
+body input {
+  font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+}
+
+body a {
+  color: #4078c0;
+  text-decoration: none;
+}
+
+body a:hover,
+body a:active {
+  text-decoration: underline;
+}
+
+body hr {
+  height: 0;
+  margin: 15px 0;
+  overflow: hidden;
+  background: transparent;
+  border: 0;
+  border-bottom: 1px solid #ddd;
+}
+
+body hr:before {
+  display: table;
+  content: "";
+}
+
+body hr:after {
+  display: table;
+  clear: both;
+  content: "";
+}
+
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6 {
+  margin-top: 15px;
+  margin-bottom: 15px;
+  line-height: 1.1;
+}
+
+body h1 {
+  font-size: 30px;
+}
+
+body h2 {
+  font-size: 21px;
+}
+
+body h3 {
+  font-size: 16px;
+}
+
+body h4 {
+  font-size: 14px;
+}
+
+body h5 {
+  font-size: 12px;
+}
+
+body h6 {
+  font-size: 11px;
+}
+
+body blockquote {
+  margin: 0;
+}
+
+body ul,
+body ol {
+  padding: 0;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+body ol ol,
+body ul ol {
+  list-style-type: lower-roman;
+}
+
+body ul ul ol,
+body ul ol ol,
+body ol ul ol,
+body ol ol ol {
+  list-style-type: lower-alpha;
+}
+
+body dd {
+  margin-left: 0;
+}
+
+body code {
+  font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  font-size: 12px;
+}
+
+body pre {
+  margin-top: 0;
+  margin-bottom: 0;
+  font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+}
+
+body .select::-ms-expand {
+  opacity: 0;
+}
+
+body .octicon {
+  font: normal normal normal 16px/1 octicons-link;
+  display: inline-block;
+  text-decoration: none;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+body .octicon-link:before {
+  content: '\f05c';
+}
+
+body:before {
+  display: table;
+  content: "";
+}
+
+body:after {
+  display: table;
+  clear: both;
+  content: "";
+}
+
+body>*:first-child {
+  margin-top: 0 !important;
+}
+
+body>*:last-child {
+  margin-bottom: 0 !important;
+}
+
+body a:not([href]) {
+  color: inherit;
+  text-decoration: none;
+}
+
+body .anchor {
+  display: inline-block;
+  padding-right: 2px;
+  margin-left: -18px;
+}
+
+body .anchor:focus {
+  outline: none;
+}
+
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6 {
+  margin-top: 1em;
+  margin-bottom: 16px;
+  font-weight: bold;
+  line-height: 1.4;
+}
+
+body h1 .octicon-link,
+body h2 .octicon-link,
+body h3 .octicon-link,
+body h4 .octicon-link,
+body h5 .octicon-link,
+body h6 .octicon-link {
+  color: #000;
+  vertical-align: middle;
+  visibility: hidden;
+}
+
+body h1:hover .anchor,
+body h2:hover .anchor,
+body h3:hover .anchor,
+body h4:hover .anchor,
+body h5:hover .anchor,
+body h6:hover .anchor {
+  text-decoration: none;
+}
+
+body h1:hover .anchor .octicon-link,
+body h2:hover .anchor .octicon-link,
+body h3:hover .anchor .octicon-link,
+body h4:hover .anchor .octicon-link,
+body h5:hover .anchor .octicon-link,
+body h6:hover .anchor .octicon-link {
+  visibility: visible;
+}
+
+body h1 {
+  padding-bottom: 0.3em;
+  font-size: 1.75em;
+  line-height: 1.2;
+}
+
+body h1 .anchor {
+  line-height: 1;
+}
+
+body h2 {
+  padding-bottom: 0.3em;
+  font-size: 1.5em;
+  line-height: 1.225;
+}
+
+body h2 .anchor {
+  line-height: 1;
+}
+
+body h3 {
+  font-size: 1.25em;
+  line-height: 1.43;
+}
+
+body h3 .anchor {
+  line-height: 1.2;
+}
+
+body h4 {
+  font-size: 1em;
+}
+
+body h4 .anchor {
+  line-height: 1.2;
+}
+
+body h5 {
+  font-size: 1em;
+}
+
+body h5 .anchor {
+  line-height: 1.1;
+}
+
+body h6 {
+  font-size: 1em;
+  color: #777;
+}
+
+body h6 .anchor {
+  line-height: 1.1;
+}
+
+body p,
+body blockquote,
+body ul,
+body ol,
+body dl,
+body table,
+body pre {
+  margin-top: 0;
+  margin-bottom: 16px;
+}
+
+body hr {
+  height: 4px;
+  padding: 0;
+  margin: 16px 0;
+  background-color: #e7e7e7;
+  border: 0 none;
+}
+
+body ul,
+body ol {
+  padding-left: 2em;
+}
+
+body ul ul,
+body ul ol,
+body ol ol,
+body ol ul {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+body li>p {
+  margin-top: 16px;
+}
+
+body dl {
+  padding: 0;
+}
+
+body dl dt {
+  padding: 0;
+  margin-top: 16px;
+  font-size: 1em;
+  font-style: italic;
+  font-weight: bold;
+}
+
+body dl dd {
+  padding: 0 16px;
+  margin-bottom: 16px;
+}
+
+body blockquote {
+  padding: 0 15px;
+  color: #777;
+  border-left: 4px solid #ddd;
+}
+
+body blockquote>:first-child {
+  margin-top: 0;
+}
+
+body blockquote>:last-child {
+  margin-bottom: 0;
+}
+
+body table {
+  display: block;
+  width: 100%;
+  overflow: auto;
+  word-break: normal;
+  word-break: keep-all;
+}
+
+body table th {
+  font-weight: bold;
+}
+
+body table th,
+body table td {
+  padding: 6px 13px;
+  border: 1px solid #ddd;
+}
+
+body table tr {
+  background-color: #fff;
+  border-top: 1px solid #ccc;
+}
+
+body table tr:nth-child(2n) {
+  background-color: #f8f8f8;
+}
+
+body img {
+  max-width: 100%;
+  box-sizing: content-box;
+  background-color: #fff;
+}
+
+body code {
+  padding: 0;
+  padding-top: 0;
+  padding-bottom: 0;
+  margin: 0;
+  font-size: 85%;
+  background-color: rgba(0,0,0,0.04);
+  border-radius: 3px;
+}
+
+body code:before,
+body code:after {
+  letter-spacing: -0.2em;
+  content: "\00a0";
+}
+
+body pre>code {
+  padding: 0;
+  margin: 0;
+  font-size: 100%;
+  word-break: normal;
+  white-space: pre;
+  background: transparent;
+  border: 0;
+}
+
+body .highlight {
+  margin-bottom: 16px;
+}
+
+body .highlight pre,
+body pre {
+  padding: 16px;
+  overflow: auto;
+  font-size: 85%;
+  line-height: 1.45;
+  background-color: #f7f7f7;
+  border-radius: 3px;
+}
+
+body .highlight pre {
+  margin-bottom: 0;
+  word-break: normal;
+}
+
+body pre {
+  word-wrap: normal;
+}
+
+body pre code {
+  display: inline;
+  max-width: initial;
+  padding: 0;
+  margin: 0;
+  overflow: initial;
+  line-height: inherit;
+  word-wrap: normal;
+  background-color: transparent;
+  border: 0;
+}
+
+body pre code:before,
+body pre code:after {
+  content: normal;
+}
+
+body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font-size: 11px;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+body .pl-c {
+  color: #969896;
+}
+
+body .pl-c1,
+body .pl-s .pl-v {
+  color: #0086b3;
+}
+
+body .pl-e,
+body .pl-en {
+  color: #795da3;
+}
+
+body .pl-s .pl-s1,
+body .pl-smi {
+  color: #333;
+}
+
+body .pl-ent {
+  color: #63a35c;
+}
+
+body .pl-k {
+  color: #a71d5d;
+}
+
+body .pl-pds,
+body .pl-s,
+body .pl-s .pl-pse .pl-s1,
+body .pl-sr,
+body .pl-sr .pl-cce,
+body .pl-sr .pl-sra,
+body .pl-sr .pl-sre {
+  color: #183691;
+}
+
+body .pl-v {
+  color: #ed6a43;
+}
+
+body .pl-id {
+  color: #b52a1d;
+}
+
+body .pl-ii {
+  background-color: #b52a1d;
+  color: #f8f8f8;
+}
+
+body .pl-sr .pl-cce {
+  color: #63a35c;
+  font-weight: bold;
+}
+
+body .pl-ml {
+  color: #693a17;
+}
+
+body .pl-mh,
+body .pl-mh .pl-en,
+body .pl-ms {
+  color: #1d3e81;
+  font-weight: bold;
+}
+
+body .pl-mq {
+  color: #008080;
+}
+
+body .pl-mi {
+  color: #333;
+  font-style: italic;
+}
+
+body .pl-mb {
+  color: #333;
+  font-weight: bold;
+}
+
+body .pl-md {
+  background-color: #ffecec;
+  color: #bd2c00;
+}
+
+body .pl-mi1 {
+  background-color: #eaffea;
+  color: #55a532;
+}
+
+body .pl-mdr {
+  color: #795da3;
+  font-weight: bold;
+}
+
+body .pl-mo {
+  color: #1d3e81;
+}
+
+body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+body .task-list-item {
+  list-style-type: none;
+}
+
+body .task-list-item+.task-list-item {
+  margin-top: 3px;
+}
+
+body .task-list-item input {
+  margin: 0 0.35em 0.25em -1.6em;
+  vertical-align: middle;
+}
+
+body :checked+.radio-label {
+  z-index: 1;
+  position: relative;
+  border-color: #4078c0;
+}
+</style>
+</head>
+  <body>
+<ul>
+<li><a href="#objectives">Objectives</a></li>
+<li><a href="#tldr">tl;dr</a></li>
+</ul>
+<h1 id="making-a-jalview-release"><a href="#making-a-jalview-release" name="making-a-jalview-release" class="anchor"><span class="octicon octicon-link"></span>Making a Jalview release</a></h1>
+<h2 id="objectives"><a href="#objectives" name="objectives" class="anchor"><span class="octicon octicon-link"></span>Objectives</a></h2>
+<p>1 Update the release getdown channel
+2 Update single executable jar
+3 Update installer
+4 Update web site with release notes
+5 Update Conda recipe
+6 Update Homebrew
+7 Update JalviewJS</p>
+<h2 id="tldr"><a href="#tldr" name="tldr" class="anchor"><span class="octicon octicon-link"></span>tl;dr</a></h2>
+<p><code>OSX_SIGNING=yes KEYPASS=MYSECRETKEY gradle -POSX_KEYPASS=true -POSX_KEYSTORE=/Users/jbb/buildtools/private/sourceofpain.p12 -PJSIGN_SH=&quot;/Users/jbb/buildtools/jsign.sh&quot; -Pinstall4j_verbose=true -PJAVA_VERSION=1.8 -PCHANNEL=RELEASE -PVERSION=W.X.Y.Z getdown installers shadowJar</code>
+<code>codesign -s &quot;MY Apple Dev Name&quot; build/install4j/1.8/Jalview-DEVELOPMENT-macos-java_8.dmg</code></p>
+
+  </body>
+</html>
index 59699e3..98c2201 100644 (file)
@@ -1,5 +1,6 @@
-org.gradle.jvmargs=-Xmx1536m -Xms512m -Dfile.encoding=UTF-8
-#systemProp.file.encoding=UTF-8
+# Convention for properties.  Read from gradle.properties, use lower_case_underlines for property names.
+# For properties set within build.gradle, use camelCaseNoSpace.
+#
 
 jalviewDir = .
 
@@ -12,7 +13,8 @@ source_dir = src
 test_source_dir = test
 #test_source_dir = utils/jalviewjs/test/test
 
-JALVIEW_VERSION = DEVELOPMENT
+# JALVIEW_VERSION now being set by logic in build.gradle defaulting to looking at jalview.version property in RELEASE file
+#JALVIEW_VERSION = DEVELOPMENT
 INSTALLATION = Source
 jalview_keystore = keys/.keystore
 jalview_keystore.pass = alignmentisfun
@@ -24,38 +26,35 @@ proxyHost = sqid
 jalview_keyalg = SHA1withRSA
 jalview_keydig = SHA1
 
-testngGroups = Functional
-testngExcludedGroups = 
+testng_groups = Functional
+testng_excluded_groups = 
 
 j8libDir = j8lib
 j11libDir = j11lib
 resource_dir = resources
 help_parent_dir = help
 help_dir = help
-docDir = doc
-schemaDir = schemas
+doc_dir = doc
 classes_dir = classes
-examplesDir = examples
 clover = false
-use_clover = false
-cloverReportJVMHeap = 2g
-cloverReportJVMArgs = -Dfile.encoding=UTF-8
-cloverReportHTMLOptions = 
-cloverReportXMLOptions =
-cloverClassesDir = clover-classes
-cloverSourcesInstrDir = sources-instr
-packageDir = dist
+clover_classes_dir = clover-classes
+clover_sources_instr_dir = clover-instr
+clover_report_dir = clover-report
+clover_lib_dir = utils/clover/lib
+cloverreport_mem = 2g
+cloverreport_jvmargs = -Dfile.encoding=UTF-8
+cloverreport_html_options = 
+cloverreport_xml_options =
+package_dir = dist
 ARCHIVEDIR =
-outputJar = jalview.jar
 
-testOutputDir = tests
-utilsDir = utils
+test_output_dir = tests
+utils_dir = utils
 
 build_properties_file = .build_properties
 application_codebase = *.jalview.org
-mainClass = jalview.bin.Jalview
-shadowJarMainClass = jalview.bin.Launcher
-launcherClass = jalview.bin.Jalview
+main_class = jalview.bin.Jalview
+shadow_jar_main_class = jalview.bin.Launcher
 
 jalview_name = Jalview
 
@@ -112,6 +111,8 @@ j11libDir = j11lib
 j11modDir = j11mod
 j11modules = com.sun.istack.runtime,com.sun.xml.bind,com.sun.xml.fastinfoset,com.sun.xml.streambuffer,com.sun.xml.txw2,com.sun.xml.ws.policy,java.activation,java.annotation,java.base,java.compiler,java.datatransfer,java.desktop,java.logging,java.management,java.management.rmi,java.naming,java.prefs,java.rmi,java.scripting,java.security.sasl,java.sql,java.xml,java.xml.bind,java.xml.soap,java.xml.ws,javax.jws,jdk.httpserver,jdk.jsobject,jdk.unsupported,jdk.xml.dom,org.jvnet.mimepull,org.jvnet.staxex,javax.servlet.api,java.ws.rs
 
+flexmark_css = utils/doc/github.css
+
 install4j_home_dir = ~/buildtools/install4j8
 install4j_copyright_message = ...
 install4j_bundle_id = org.jalview.jalview-desktop
@@ -130,9 +131,6 @@ OSX_KEYSTORE =
 OSX_KEYPASS =
 JSIGN_SH = echo
 
-pandoc_exec = /usr/local/bin/pandoc,/usr/bin/pandoc
-dev = false
-
 CHANNEL=LOCAL
 getdown_channel_base = https://www.jalview.org/getdown
 getdown_app_dir_release = release
@@ -164,7 +162,6 @@ jalviewjs_j2s_plugin = swingjs/net.sf.j2s.core-j11.jar
 jalviewjs_libjs_dir = utils/jalviewjs/libjs
 jalviewjs_site_resource_dir = utils/jalviewjs/site-resources
 jalviewjs_classlists_dir = utils/jalviewjs/classlists
-jalviewjs_classlist_jalview = utils/jalviewjs/_j2sclasslist.txt
 jalviewjs_j2s_settings = .j2s
 #jalviewjs_eclipse_workspace = ~/tmp/eclipse-workspace
 # these 3 files/dirs found in build/jalviewjs
@@ -180,14 +177,12 @@ jalviewjs_eclipse_build_arg = cleanBuild
 jalviewjs_server_port = 9001
 jalviewjs_server_wait = 30
 jalviewjs_server_resource = /jalview_bin_Jalview.html
-jalviewjs_core_name = _jalview
 jalviewjs_name = JalviewJS
 jalviewjs_core_key = core
 #jalviewjs_core_key = preloadCore
 jalviewjs_ignore_transpile_errors = true
 
 j2s.compiler.status = enable
-j2s.compiler.java.version = 11
 #j2s.site.directory = null ## site defined from buildDir+'/jalviewjs/'+jalviewjs_site_dir
 #j2s.log.methods.declared = j2s_methods_declared.log
 #j2s.log.methods.called = j2s_methods_called.log
index 5b6180d..c83741a 100755 (executable)
@@ -57,6 +57,21 @@ li:before {
     </tr>
     <tr>
       <td width="60" align="center" nowrap><strong><a
+          id="Jalview.2.11.1">2.11.1</a><a id="Jalview.2.11.1.1">.1</a><br />
+          <em>13/07/2020</em></strong></td>
+      <td align="left" valign="top">
+        <ul>
+        <!-- -->
+       </ul>
+      </td>
+      <td align="left" valign="top">
+        <ul>
+         <li><!-- JAL-3493 -->Escape does not clear highlights on the alignment (Since Jalview 2.10.3)</li>
+       </ul>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" align="center" nowrap><strong><a
           id="Jalview.2.11.1">2.11.1</a><a id="Jalview.2.11.1.0">.0</a><br />
           <em>22/04/2020</em></strong></td>
       <td align="left" valign="top">
diff --git a/j11lib/Jmol-14.29.17-no_netscape.jar b/j11lib/Jmol-14.29.17-no_netscape.jar
deleted file mode 100644 (file)
index 5e7d573..0000000
Binary files a/j11lib/Jmol-14.29.17-no_netscape.jar and /dev/null differ
diff --git a/j11lib/Jmol-15.1.3.jar b/j11lib/Jmol-15.1.3.jar
new file mode 100644 (file)
index 0000000..1672e67
Binary files /dev/null and b/j11lib/Jmol-15.1.3.jar differ
diff --git a/j11lib/intervalstore-v1.0.jar b/j11lib/intervalstore-v1.0.jar
deleted file mode 100644 (file)
index 4f6101c..0000000
Binary files a/j11lib/intervalstore-v1.0.jar and /dev/null differ
diff --git a/j11lib/intervalstore-v1.1.jar b/j11lib/intervalstore-v1.1.jar
new file mode 100644 (file)
index 0000000..668b543
Binary files /dev/null and b/j11lib/intervalstore-v1.1.jar differ
diff --git a/j8lib/Jmol-14.29.17.jar b/j8lib/Jmol-14.29.17.jar
deleted file mode 100644 (file)
index 136eb93..0000000
Binary files a/j8lib/Jmol-14.29.17.jar and /dev/null differ
diff --git a/j8lib/Jmol-15.1.3.jar b/j8lib/Jmol-15.1.3.jar
new file mode 100644 (file)
index 0000000..1672e67
Binary files /dev/null and b/j8lib/Jmol-15.1.3.jar differ
diff --git a/j8lib/intervalstore-v1.0.jar b/j8lib/intervalstore-v1.0.jar
deleted file mode 100644 (file)
index 4f6101c..0000000
Binary files a/j8lib/intervalstore-v1.0.jar and /dev/null differ
diff --git a/j8lib/intervalstore-v1.1.jar b/j8lib/intervalstore-v1.1.jar
new file mode 100644 (file)
index 0000000..668b543
Binary files /dev/null and b/j8lib/intervalstore-v1.1.jar differ
index 53d767b..c1d04c4 100644 (file)
@@ -35,6 +35,7 @@ package ext.edu.ucsf.rbvi.strucviz2;
 import jalview.bin.Cache;
 import jalview.gui.Preferences;
 import jalview.util.FileUtils;
+import jalview.util.Platform;
 
 import java.io.File;
 import java.io.IOException;
@@ -932,7 +933,7 @@ public class StructureManager
       pathList.add("/usr/local/chimera/bin/chimera");
       pathList.add("/usr/local/bin/chimera");
       pathList.add("/usr/bin/chimera");
-      pathList.add(System.getProperty("user.home") + "/opt/bin/chimera");
+      pathList.add(Platform.getUserPath("opt/bin/chimera"));
     }
     else if (os.startsWith("Windows"))
     {
index 8a387c7..f2b2222 100755 (executable)
@@ -22,6 +22,8 @@ package jalview.analysis;
 
 import jalview.analysis.scoremodels.PIDModel;
 import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
@@ -32,6 +34,7 @@ import jalview.datamodel.SequenceNode;
 import jalview.util.QuickSort;
 
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -51,40 +54,64 @@ import java.util.List;
  * from the first tobesorted position in the alignment. e.g. (a,tb2,b,tb1,c,tb3
  * becomes a,tb1,tb2,tb3,b,c)
  */
-public class AlignmentSorter
+public class AlignmentSorter implements ApplicationSingletonI
 {
+
+  private AlignmentSorter()
+  {
+    // private singleton
+  }
+
+  public static AlignmentSorter getInstance()
+  {
+    return (AlignmentSorter) ApplicationSingletonProvider
+            .getInstance(AlignmentSorter.class);
+  }
+
+  /**
+   * types of feature ordering: Sort by score : average score - or total score -
+   * over all features in region Sort by feature label text: (or if null -
+   * feature type text) - numerical or alphabetical Sort by feature density:
+   * based on counts - ignoring individual text or scores for each feature
+   */
+  public static final String FEATURE_SCORE = "average_score";
+
+  public static final String FEATURE_LABEL = "text";
+
+  public static final String FEATURE_DENSITY = "density";
+
   /*
    * todo: refactor searches to follow a basic pattern: (search property, last
    * search state, current sort direction)
    */
-  static boolean sortIdAscending = true;
+  boolean sortIdAscending = true;
 
-  static int lastGroupHash = 0;
+  int lastGroupHash = 0;
 
-  static boolean sortGroupAscending = true;
+  boolean sortGroupAscending = true;
 
-  static AlignmentOrder lastOrder = null;
+  AlignmentOrder lastOrder = null;
 
-  static boolean sortOrderAscending = true;
+  boolean sortOrderAscending = true;
 
-  static TreeModel lastTree = null;
+  TreeModel lastTree = null;
 
-  static boolean sortTreeAscending = true;
+  boolean sortTreeAscending = true;
 
-  /*
+  /**
    * last Annotation Label used for sort by Annotation score
    */
-  private static String lastSortByAnnotation;
+  private String lastSortByAnnotation;
 
-  /*
-   * string hash of last arguments to sortByFeature
-   * (sort order toggles if this is unchanged between sorts)
+  /**
+   * string hash of last arguments to sortByFeature (sort order toggles if this
+   * is unchanged between sorts)
    */
-  private static String sortByFeatureCriteria;
+  private String sortByFeatureCriteria;
 
-  private static boolean sortByFeatureAscending = true;
+  private boolean sortByFeatureAscending = true;
 
-  private static boolean sortLengthAscending;
+  private boolean sortLengthAscending;
 
   private static boolean sortEValueAscending;
 
@@ -119,93 +146,10 @@ public class AlignmentSorter
     }
 
     QuickSort.sort(scores, seqs);
-
     setReverseOrder(align, seqs);
   }
 
   /**
-   * Reverse the order of the sort
-   * 
-   * @param align
-   *          DOCUMENT ME!
-   * @param seqs
-   *          DOCUMENT ME!
-   */
-  private static void setReverseOrder(AlignmentI align, SequenceI[] seqs)
-  {
-    int nSeq = seqs.length;
-
-    int len = 0;
-
-    if ((nSeq % 2) == 0)
-    {
-      len = nSeq / 2;
-    }
-    else
-    {
-      len = (nSeq + 1) / 2;
-    }
-
-    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
-    List<SequenceI> asq = align.getSequences();
-    synchronized (asq)
-    {
-      for (int i = 0; i < len; i++)
-      {
-        // SequenceI tmp = seqs[i];
-        asq.set(i, seqs[nSeq - i - 1]);
-        asq.set(nSeq - i - 1, seqs[i]);
-      }
-    }
-  }
-
-  /**
-   * Sets the Alignment object with the given sequences
-   * 
-   * @param align
-   *          Alignment object to be updated
-   * @param tmp
-   *          sequences as a vector
-   */
-  private static void setOrder(AlignmentI align, List<SequenceI> tmp)
-  {
-    setOrder(align, vectorSubsetToArray(tmp, align.getSequences()));
-  }
-
-  /**
-   * Sets the Alignment object with the given sequences
-   * 
-   * @param align
-   *          DOCUMENT ME!
-   * @param seqs
-   *          sequences as an array
-   */
-  public static void setOrder(AlignmentI align, SequenceI[] seqs)
-  {
-    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
-    List<SequenceI> algn = align.getSequences();
-    synchronized (algn)
-    {
-      List<SequenceI> tmp = new ArrayList<>();
-
-      for (int i = 0; i < seqs.length; i++)
-      {
-        if (algn.contains(seqs[i]))
-        {
-          tmp.add(seqs[i]);
-        }
-      }
-
-      algn.clear();
-      // User may have hidden seqs, then clicked undo or redo
-      for (int i = 0; i < tmp.size(); i++)
-      {
-        algn.add(tmp.get(i));
-      }
-    }
-  }
-
-  /**
    * Sorts by ID. Numbers are sorted before letters.
    * 
    * @param align
@@ -225,17 +169,9 @@ public class AlignmentSorter
     }
 
     QuickSort.sort(ids, seqs);
-
-    if (sortIdAscending)
-    {
-      setReverseOrder(align, seqs);
-    }
-    else
-    {
-      setOrder(align, seqs);
-    }
-
-    sortIdAscending = !sortIdAscending;
+    AlignmentSorter as = getInstance();
+    as.sortIdAscending = !as.sortIdAscending;
+    set(align, seqs, as.sortIdAscending);
   }
 
   /**
@@ -258,17 +194,9 @@ public class AlignmentSorter
     }
 
     QuickSort.sort(length, seqs);
-
-    if (sortLengthAscending)
-    {
-      setReverseOrder(align, seqs);
-    }
-    else
-    {
-      setOrder(align, seqs);
-    }
-
-    sortLengthAscending = !sortLengthAscending;
+    AlignmentSorter as = getInstance();
+    as.sortLengthAscending = !as.sortLengthAscending;
+    set(align, seqs, as.sortLengthAscending);
   }
 
   /**
@@ -369,14 +297,16 @@ public class AlignmentSorter
     // ORDERS BY GROUP SIZE
     List<SequenceGroup> groups = new ArrayList<>();
 
-    if (groups.hashCode() != lastGroupHash)
+    AlignmentSorter as = getInstance();
+
+    if (groups.hashCode() != as.lastGroupHash)
     {
-      sortGroupAscending = true;
-      lastGroupHash = groups.hashCode();
+      as.sortGroupAscending = true;
+      as.lastGroupHash = groups.hashCode();
     }
     else
     {
-      sortGroupAscending = !sortGroupAscending;
+      as.sortGroupAscending = !as.sortGroupAscending;
     }
 
     // SORTS GROUPS BY SIZE
@@ -403,7 +333,7 @@ public class AlignmentSorter
 
     // NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER
     // /////////////////////////////////////////////
-    List<SequenceI> seqs = new ArrayList<>();
+    List<SequenceI> tmp = new ArrayList<>();
 
     for (int i = 0; i < groups.size(); i++)
     {
@@ -412,68 +342,10 @@ public class AlignmentSorter
 
       for (int j = 0; j < orderedseqs.length; j++)
       {
-        seqs.add(orderedseqs[j]);
-      }
-    }
-
-    if (sortGroupAscending)
-    {
-      setOrder(align, seqs);
-    }
-    else
-    {
-      setReverseOrder(align,
-              vectorSubsetToArray(seqs, align.getSequences()));
-    }
-  }
-
-  /**
-   * Select sequences in order from tmp that is present in mask, and any
-   * remaining sequences in mask not in tmp
-   * 
-   * @param tmp
-   *          thread safe collection of sequences
-   * @param mask
-   *          thread safe collection of sequences
-   * 
-   * @return intersect(tmp,mask)+intersect(complement(tmp),mask)
-   */
-  private static SequenceI[] vectorSubsetToArray(List<SequenceI> tmp,
-          List<SequenceI> mask)
-  {
-    // or?
-    // tmp2 = tmp.retainAll(mask);
-    // return tmp2.addAll(mask.removeAll(tmp2))
-
-    ArrayList<SequenceI> seqs = new ArrayList<>();
-    int i, idx;
-    boolean[] tmask = new boolean[mask.size()];
-
-    for (i = 0; i < mask.size(); i++)
-    {
-      tmask[i] = true;
-    }
-
-    for (i = 0; i < tmp.size(); i++)
-    {
-      SequenceI sq = tmp.get(i);
-      idx = mask.indexOf(sq);
-      if (idx > -1 && tmask[idx])
-      {
-        tmask[idx] = false;
-        seqs.add(sq);
-      }
-    }
-
-    for (i = 0; i < tmask.length; i++)
-    {
-      if (tmask[i])
-      {
-        seqs.add(mask.get(i));
+        tmp.add(orderedseqs[j]);
       }
     }
-
-    return seqs.toArray(new SequenceI[seqs.size()]);
+    set(align, tmp, as.sortGroupAscending);
   }
 
   /**
@@ -489,24 +361,17 @@ public class AlignmentSorter
     // Get an ordered vector of sequences which may also be present in align
     List<SequenceI> tmp = order.getOrder();
 
-    if (lastOrder == order)
-    {
-      sortOrderAscending = !sortOrderAscending;
-    }
-    else
-    {
-      sortOrderAscending = true;
-    }
+    AlignmentSorter as = getInstance();
 
-    if (sortOrderAscending)
+    if (as.lastOrder == order)
     {
-      setOrder(align, tmp);
+      as.sortOrderAscending = !as.sortOrderAscending;
     }
     else
     {
-      setReverseOrder(align,
-              vectorSubsetToArray(tmp, align.getSequences()));
+      as.sortOrderAscending = true;
     }
+    set(align, tmp, as.sortOrderAscending);
   }
 
   /**
@@ -561,26 +426,19 @@ public class AlignmentSorter
   {
     List<SequenceI> tmp = getOrderByTree(align, tree);
 
-    // tmp should properly permute align with tree.
-    if (lastTree != tree)
-    {
-      sortTreeAscending = true;
-      lastTree = tree;
-    }
-    else
-    {
-      sortTreeAscending = !sortTreeAscending;
-    }
+    AlignmentSorter as = getInstance();
 
-    if (sortTreeAscending)
+    // tmp should properly permute align with tree.
+    if (as.lastTree != tree)
     {
-      setOrder(align, tmp);
+      as.sortTreeAscending = true;
+      as.lastTree = tree;
     }
     else
     {
-      setReverseOrder(align,
-              vectorSubsetToArray(tmp, align.getSequences()));
+      as.sortTreeAscending = !as.sortTreeAscending;
     }
+    set(align, tmp, as.sortTreeAscending);
   }
 
   /**
@@ -746,9 +604,12 @@ public class AlignmentSorter
     }
 
     jalview.util.QuickSort.sort(scores, seqs);
-    if (lastSortByAnnotation != scoreLabel)
+
+    AlignmentSorter as = getInstance();
+
+    if (as.lastSortByAnnotation != scoreLabel)
     {
-      lastSortByAnnotation = scoreLabel;
+      as.lastSortByAnnotation = scoreLabel;
       setOrder(alignment, seqs);
     }
     else
@@ -758,18 +619,6 @@ public class AlignmentSorter
   }
 
   /**
-   * types of feature ordering: Sort by score : average score - or total score -
-   * over all features in region Sort by feature label text: (or if null -
-   * feature type text) - numerical or alphabetical Sort by feature density:
-   * based on counts - ignoring individual text or scores for each feature
-   */
-  public static String FEATURE_SCORE = "average_score";
-
-  public static String FEATURE_LABEL = "text";
-
-  public static String FEATURE_DENSITY = "density";
-
-  /**
    * Sort sequences by feature score or density, optionally restricted by
    * feature types, feature groups, or alignment start/end positions.
    * <p>
@@ -796,14 +645,15 @@ public class AlignmentSorter
     if (method != FEATURE_SCORE && method != FEATURE_LABEL
             && method != FEATURE_DENSITY)
     {
-      String msg = String
-              .format("Implementation Error - sortByFeature method must be either '%s' or '%s'",
-                      FEATURE_SCORE, FEATURE_DENSITY);
+      String msg = String.format(
+              "Implementation Error - sortByFeature method must be either '%s' or '%s'",
+              FEATURE_SCORE, FEATURE_DENSITY);
       System.err.println(msg);
       return;
     }
 
-    flipFeatureSortIfUnchanged(method, featureTypes, groups, startCol, endCol);
+    flipFeatureSortIfUnchanged(method, featureTypes, groups, startCol,
+            endCol);
 
     SequenceI[] seqs = alignment.getSequencesArray();
 
@@ -822,8 +672,8 @@ public class AlignmentSorter
        * get sequence residues overlapping column region
        * and features for residue positions and specified types
        */
-      String[] types = featureTypes == null ? null : featureTypes
-              .toArray(new String[featureTypes.size()]);
+      String[] types = featureTypes == null ? null
+              : featureTypes.toArray(new String[featureTypes.size()]);
       List<SequenceFeature> sfs = seqs[i].findFeatures(startCol + 1,
               endCol + 1, types);
 
@@ -897,6 +747,8 @@ public class AlignmentSorter
       }
     }
 
+    boolean doSort = false;
+
     if (FEATURE_SCORE.equals(method))
     {
       if (hasScores == 0)
@@ -922,7 +774,7 @@ public class AlignmentSorter
           }
         }
       }
-      QuickSort.sortByDouble(scores, seqs, sortByFeatureAscending);
+      doSort = true;
     }
     else if (FEATURE_DENSITY.equals(method))
     {
@@ -934,9 +786,13 @@ public class AlignmentSorter
         // System.err.println("Sorting on Density: seq "+seqs[i].getName()+
         // " Feats: "+featureCount+" Score : "+scores[i]);
       }
-      QuickSort.sortByDouble(scores, seqs, sortByFeatureAscending);
+      doSort = true;
+    }
+    if (doSort)
+    {
+      QuickSort.sortByDouble(scores, seqs,
+              getInstance().sortByFeatureAscending);
     }
-
     setOrder(alignment, seqs);
   }
 
@@ -971,16 +827,177 @@ public class AlignmentSorter
     /*
      * if resorting on the same criteria, toggle sort order
      */
-    if (sortByFeatureCriteria == null
-            || !scoreCriteria.equals(sortByFeatureCriteria))
+    AlignmentSorter as = getInstance();
+    if (as.sortByFeatureCriteria == null
+            || !scoreCriteria.equals(as.sortByFeatureCriteria))
     {
-      sortByFeatureAscending = true;
+      as.sortByFeatureAscending = true;
     }
     else
     {
-      sortByFeatureAscending = !sortByFeatureAscending;
+      as.sortByFeatureAscending = !as.sortByFeatureAscending;
     }
-    sortByFeatureCriteria = scoreCriteria;
+    as.sortByFeatureCriteria = scoreCriteria;
+  }
+
+  /**
+   * Set the alignment's sequences list to contain the sequences from a
+   * temporary list, first adding all the elements from the tmp list, then adding all sequences in the alignment that
+   * are not in the list. Option to do the final sort either in order or in reverse order.
+   * 
+   * @param align The alignment being sorted
+   * @param tmp
+   *          the temporary sequence list
+   * @param ascending
+   *          false for reversed order; only sequences already in
+   *          the alignment will be used (which is actually already guaranteed
+   *          by vectorSubsetToArray)
+   */
+  private static void set(AlignmentI align, List<SequenceI> tmp,
+          boolean ascending)
+  {
+    set(align, vectorSubsetToArray(align.getSequences(), tmp), ascending);
+  }
+
+  /**
+   * Set the alignment's sequences list to contain these sequences, either in
+   * this order or its reverse.
+   * 
+   * @param align
+   * @param seqs
+   *          the new sequence array
+   * @param ascending
+   *          false for reversed order; if ascending, only sequences already in
+   *          the alignment will be used; if descending, then a direct 1:1
+   *          replacement is made
+   */
+  private static void set(AlignmentI align, SequenceI[] seqs,
+          boolean ascending)
+  {
+    if (ascending)
+    {
+      setOrder(align, seqs);
+    }
+    else
+    {
+      setReverseOrder(align, seqs);
+    }
+
+  }
+
+  /**
+   * Replace the alignment's sequences with values in an array, clearing the
+   * alignment's sequence list and filtering for sequences that are actually in
+   * the alignment already.
+   * 
+   * @param align
+   *          the Alignment
+   * @param seqs
+   *          the array of replacement values, of any length
+   */
+  public static void setOrder(AlignmentI align, SequenceI[] seqs)
+  {
+    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
+    List<SequenceI> seqList = align.getSequences();
+    synchronized (seqList)
+    {
+      List<SequenceI> tmp = new ArrayList<>();
+
+      for (int i = 0; i < seqs.length; i++)
+      {
+        if (seqList.contains(seqs[i]))
+        {
+          tmp.add(seqs[i]);
+        }
+      }
+
+      seqList.clear();
+      // User may have hidden seqs, then clicked undo or redo
+      for (int i = 0; i < tmp.size(); i++)
+      {
+        seqList.add(tmp.get(i));
+      }
+    }
+  }
+
+  /**
+   * Replace the alignment's sequences or a subset of those sequences with
+   * values in an array in reverse order. All sequences are replaced; no check
+   * is made that these sequences are in the alignment already.
+   * 
+   * @param align
+   *          the Alignment
+   * @param seqs
+   *          the array of replacement values, length must be less than or equal
+   *          to Alignment.sequences.size()
+   */
+  private static void setReverseOrder(AlignmentI align, SequenceI[] seqs)
+  {
+    int nSeq = seqs.length;
+
+    int len = (nSeq + (nSeq % 2)) / 2;
+    // int len = 0;
+    //
+    // if ((nSeq % 2) == 0)
+    // {
+    // len = nSeq / 2;
+    // }
+    // else
+    // {
+    // len = (nSeq + 1) / 2;
+    // }
+
+    // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work
+    List<SequenceI> seqList = align.getSequences();
+    synchronized (seqList)
+    {
+      for (int i = 0; i < len; i++)
+      {
+        // SequenceI tmp = seqs[i];
+        seqList.set(i, seqs[nSeq - i - 1]);
+        seqList.set(nSeq - i - 1, seqs[i]);
+      }
+    }
+  }
+
+  /**
+   * Create and array of reordered sequences in order first from tmp that are
+   * present in seqList already, then, after that, any remaining sequences in
+   * seqList not in tmp. Any sequences in tmp that are not in seqList already
+   * are discarded.
+   * 
+   * @param seqList
+   *          thread safe collection of sequences originally in the alignment
+   * @param tmp
+   *          thread safe collection of sequences or subsequences possibly in
+   *          seqList
+   * 
+   * @return intersect(tmp,seqList)+intersect(complement(tmp),seqList)
+   */
+  private static SequenceI[] vectorSubsetToArray(List<SequenceI> seqList,
+          List<SequenceI> tmp)
+  {
+    ArrayList<SequenceI> seqs = new ArrayList<>();
+    int n = seqList.size();
+    BitSet bs = new BitSet(n);
+    bs.set(0, n);
+    for (int i = 0, nt = tmp.size(); i < nt; i++)
+    {
+      SequenceI sq = tmp.get(i);
+      int idx = seqList.indexOf(sq);
+      if (idx >= 0 && bs.get(idx))
+      {
+        seqs.add(sq);
+        bs.clear(idx);
+      }
+    }
+
+    for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
+    {
+      seqs.add(seqList.get(i));
+    }
+
+    return seqs.toArray(new SequenceI[seqs.size()]);
   }
 
 }
index c54357e..1044802 100644 (file)
  */
 package jalview.analysis;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -31,12 +35,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
-import jalview.ws.SequenceFetcherFactory;
-import jalview.ws.seqfetcher.ASequenceFetcher;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import jalview.ws.SequenceFetcher;
 
 /**
  * Functions for cross-referencing sequence databases.
@@ -402,7 +401,6 @@ public class CrossRef
   private void retrieveCrossRef(List<DBRefEntry> sourceRefs, SequenceI seq,
           List<DBRefEntry> xrfs, boolean fromDna, AlignedCodonFrame cf)
   {
-    ASequenceFetcher sftch = SequenceFetcherFactory.getSequenceFetcher();
     SequenceI[] retrieved = null;
     SequenceI dss = seq.getDatasetSequence() == null ? seq
             : seq.getDatasetSequence();
@@ -418,7 +416,7 @@ public class CrossRef
     }
     try
     {
-      retrieved = sftch.getSequences(sourceRefs, !fromDna);
+      retrieved = SequenceFetcher.getInstance().getSequences(sourceRefs, !fromDna);
     } catch (Exception e)
     {
       System.err.println(
index 4cc5697..3fbcee2 100644 (file)
  */
 package jalview.analysis;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignedCodon;
 import jalview.datamodel.AlignedCodonFrame;
@@ -40,12 +46,6 @@ import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.util.ShiftList;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-
 public class Dna
 {
   private static final String STOP_ASTERIX = "*";
index 3cbef6d..f21d7da 100644 (file)
@@ -31,6 +31,7 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.VisibleContigsIterator;
 import jalview.util.Comparison;
+import jalview.util.Platform;
 
 import java.util.List;
 import java.util.Vector;
@@ -129,7 +130,7 @@ public class Finder implements FinderI
   {
     String searchString = matchCase ? theSearchString
             : theSearchString.toUpperCase();
-    Regex searchPattern = new Regex(searchString);
+    Regex searchPattern = Platform.newRegex(searchString);
     searchPattern.setIgnoreCase(!matchCase);
 
     searchResults = new SearchResults();
index 4b68d93..bfd4d6a 100644 (file)
@@ -23,6 +23,7 @@ package jalview.analysis;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
+import jalview.util.Platform;
 
 import com.stevesoft.pat.Regex;
 
@@ -90,7 +91,7 @@ public class ParseProperties
           String[] ScoreDescriptions, String regex, boolean repeat)
   {
     int count = 0;
-    Regex pattern = new Regex(regex);
+    Regex pattern = Platform.newRegex(regex);
     if (pattern.numSubs() > ScoreNames.length)
     {
       // Check that we have enough labels and descriptions for any parsed
index ebc9a26..8700ec0 100644 (file)
@@ -22,6 +22,8 @@ package jalview.analysis.scoremodels;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.api.analysis.ScoreModelI;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.io.DataSourceType;
 import jalview.io.FileParse;
 import jalview.io.ScoreMatrixFile;
@@ -33,18 +35,8 @@ import java.util.Map;
 /**
  * A class that can register and serve instances of ScoreModelI
  */
-public class ScoreModels
+public class ScoreModels implements ApplicationSingletonI
 {
-  private final ScoreMatrix BLOSUM62;
-
-  private final ScoreMatrix PAM250;
-
-  private final ScoreMatrix DNA;
-
-  private static ScoreModels instance;
-
-  private Map<String, ScoreModelI> models;
-
   /**
    * Answers the singleton instance of this class, with lazy initialisation
    * (built-in score models are loaded on the first call to this method)
@@ -53,11 +45,7 @@ public class ScoreModels
    */
   public static ScoreModels getInstance()
   {
-    if (instance == null)
-    {
-      instance = new ScoreModels();
-    }
-    return instance;
+    return (ScoreModels) ApplicationSingletonProvider.getInstance(ScoreModels.class);
   }
 
   /**
@@ -84,6 +72,14 @@ public class ScoreModels
     registerScoreModel(new FeatureDistanceModel());
   }
 
+  private final ScoreMatrix BLOSUM62;
+
+  private final ScoreMatrix PAM250;
+
+  private final ScoreMatrix DNA;
+
+  private Map<String, ScoreModelI> models;
+
   /**
    * Tries to load a score matrix from the given resource file, and if
    * successful, registers it.
@@ -153,7 +149,7 @@ public class ScoreModels
    */
   public void reset()
   {
-    instance = new ScoreModels();
+    ApplicationSingletonProvider.removeInstance(this.getClass());
   }
 
   /**
diff --git a/src/jalview/api/JalviewJSApi.java b/src/jalview/api/JalviewJSApi.java
new file mode 100644 (file)
index 0000000..895fd15
--- /dev/null
@@ -0,0 +1,412 @@
+package jalview.api;
+
+import java.net.URL;
+
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+
+/**
+ * JAL-3369 JalviewJS API BH 2019.07.17
+ * 
+ * @author hansonr
+ *
+ */
+public interface JalviewJSApi
+{
+
+  /**
+   * bind a pdb file to a sequence in the given AlignFrame.
+   * 
+   * @param sequenceId
+   *          - sequenceId within the dataset or null
+   * @param pdbId
+   *          - the four-character PDB ID
+   * @param pdbFile
+   *          - pdb file - either a URL or a valid PDB file or null.
+   * @param alFrame
+   *          - null or specific AlignFrame. This specifies the dataset that
+   *          will be searched for a seuqence called sequenceId
+   * 
+   * @return true if binding was success
+   */
+  public boolean addPdbFile(String sequenceId, String pdbId, String pdbFile,
+          AlignFrame alFrame);
+
+  /**
+   * Get alignment as format with or without the jalview start-end sequence
+   * suffix appended.
+   * 
+   * @param format
+   * @param addSuffix
+   *          (default false)
+   * @param alf
+   *          (default current)
+   * 
+   * @return
+   */
+  public String getAlignment(String format, boolean addSuffix,
+          AlignFrame alf);
+
+  /**
+   * Get an array of sequence IDs reflecting the order of the alignment in the
+   * specified alignment frame
+   * 
+   * @param alf
+   *          (default current)
+   * @return array of sequence IDs
+   */
+  public String[] getAlignmentOrder(AlignFrame alf);
+
+  /**
+   * Get alignment view alf's annotation as an annotation file
+   * 
+   * @param alf
+   *          (default current)
+   * @return annotation
+   */
+  public String getAnnotation(AlignFrame alf);
+
+  /**
+   * Get the URL for the location where the code is found; typically ending in
+   * "swingjs/j2s".
+   * 
+   * @return web page URL
+   */
+  public URL getCodeBase();
+
+  AlignFrame getCurrentAlignFrame();
+
+  /**
+   * Get the URL for the hosting web page.
+   * 
+   * @return web page URL
+   */
+  public URL getDocumentBase();
+
+  /**
+   * Get the array of feature groups for an alignment frame.
+   * 
+   * @param alf
+   *          AlignFrame to get feature groups for (default current)
+   * @return
+   */
+  public String[] getFeatureGroups(AlignFrame alf);
+
+  /**
+   * Get the array of feature groups for an alignment frame with a specific
+   * on/off state.
+   * 
+   * @param visible
+   *          (default off)
+   * @param alf
+   *          align frame (default current)
+   * 
+   * @return
+   */
+  public String[] getFeatureGroupsOfState(boolean visible, AlignFrame alf);
+
+  /**
+   * Get the sequence features in the alignment frame in the given format
+   * (Jalview or GFF). Two additional options can be added to the format, each
+   * starting with a semicolon:
+   * 
+   * ;includeNonpositional (default) or ;positionalOnly
+   * 
+   * ;includeComplement
+   * 
+   * @param format
+   *          case-insensitive "Jalview" or "GFF" (default "GFF")
+   * @param alf
+   *          (default current)
+   * @return formatted sequence features
+   */
+  public String getFeatures(String format, AlignFrame alf);
+
+  /**
+   * Get an applet parameter as a string.
+   * 
+   * @param name
+   * @return value or null
+   */
+  public String getParameter(String name);
+
+  /**
+   * Get an applet parameter object value.
+   * 
+   * @param name
+   * @return value or null
+   */
+  public Object getParameterAsObject(String name);
+
+  /**
+   * @param alf
+   *          AlignFrame containing selection
+   * @return String list of selected sequence IDs, each terminated by current
+   *         default separator sequence
+   * 
+   */
+  public SequenceI[] getSelectedSequences(AlignFrame alf);
+
+  // BH incompatibility here -- JalviewLite created an AlignFrame; Jalview
+  // creates an AlignmentPanel
+  // /**
+  // * create a new view and return the AlignFrame instance
+  // *
+  // * @return
+  // */
+  //
+  // public AlignFrame newView();
+  //
+  // /**
+  // * create a new view named name and return the AlignFrame instance
+  // *
+  // * @param name
+  // * @return
+  // */
+  //
+  // public AlignFrame newView(String name);
+  //
+  // /**
+  // * create a new view on alf and return the AlignFrame instance
+  // *
+  // * @param alf
+  // * @return
+  // */
+  // public AlignFrame newViewFrom(AlignFrame alf);
+  //
+  // /**
+  // * create a new view named name on alf
+  // *
+  // * @param alf
+  // * @param name
+  // * @return
+  // */
+  // public AlignFrame newViewFrom(AlignFrame alf, String name);
+  //
+
+  /**
+   * get sequences selected in alf and return their alignment in format 'format'
+   * either with or without suffix
+   * 
+   * @param format
+   *          - format of alignment file
+   * @param alf
+   *          - where selection is
+   * @param suffix
+   *          - true to append /start-end string to each sequence ID
+   * 
+   * @return selected sequences as flat file or empty string if there was no
+   *         current selection
+   */
+  public String getSelectedSequencesAsAlignment(String format,
+          boolean addSuffix, AlignFrame alf);
+
+  /**
+   * 
+   * @param sequenceId
+   *          id of sequence to highlight
+   * @param position
+   *          integer position [ tobe implemented or range ] on sequence
+   * @param alignedPosition
+   *          false, blank or something else - indicate if position is an
+   *          alignment column or unaligned sequence position
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void highlight(String sequenceId, String position,
+          String alignedPosition, AlignFrame alf);
+
+  /**
+   * 
+   * @param data
+   *          alignment data as a string
+   * @param title
+   *          window title
+   * @param width
+   *          desired width or 0 for default width
+   * @param height
+   *          desired height or 0 for default height
+   * @return null or new alignment frame
+   */
+
+  public AlignFrame loadAlignment(String data, String title, int width,
+          int height);
+
+  /**
+   * add the given features or annotation to the given alignment view
+   * 
+   * @param annotation
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void loadAnnotation(String annotation, AlignFrame alf);
+
+  /**
+   * Parse the given string as a jalview feature or GFF annotation file and
+   * optionally enable feature display on the given AlignFrame.
+   * 
+   * @param features
+   *          - gff or features file
+   * @param autoenabledisplay
+   *          - when true, feature display will be enabled if any features can
+   *          be parsed from the string.
+   * @param alf
+   *          alignment frame (default current)
+   * 
+   * @return true if data parsed as features
+   */
+  public boolean loadFeatures(String features, boolean autoenabledisplay,
+          AlignFrame alf);
+
+  /**
+   * Load a score file.
+   * 
+   * @param sScoreFile
+   * @param alf
+   *          alignment frame (default current)
+   * 
+   * @return
+   */
+  public boolean loadScoreFile(String sScoreFile, AlignFrame alf);
+
+  /**
+   * public static method for JalviewJS API to open a PCAPanel without
+   * necessarily using a dialog.
+   * 
+   * @param modelName
+   * @param alf
+   *          may be null
+   * 
+   * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
+   *         if number of sequences selected is inappropriate
+   */
+  public Object openPcaPanel(String modelName, AlignFrame alf);
+
+  /**
+   * Open a new Tree panel on the desktop statically. Params are standard (not
+   * set by Groovy). No dialog is opened.
+   * 
+   * @param treeType
+   * @param modelName
+   * @param alf
+   *          align frame (default current)
+   * 
+   * @return null, or the string "label.you_need_at_least_n_sequences" if number
+   *         of sequences selected is inappropriate
+   */
+  public Object openTreePanel(String treeType, String modelName,
+          AlignFrame alf);
+
+  /**
+   * re-order the given alignment using the given array of sequence IDs
+   * 
+   * @param ids
+   *          array of sequence IDs
+   * @param undoName
+   *          - string to use when referring to ordering action in undo buffer
+   * @param alf
+   *          alignment frame (default current)
+   * 
+   * @return 'true' if alignment was actually reordered. empty string if
+   *         alignment did not contain sequences.
+   */
+  public boolean orderAlignment(String[] ids, String undoName,
+          AlignFrame alf);
+
+  /**
+   * process commandline arguments after the JavaScript application has started
+   *
+   * @param args
+   * @return
+   */
+  public Object parseArguments(String[] args);
+
+  boolean parseFeaturesFile(String filename, AlignFrame alf);
+
+  // Bob's additions:
+
+  /**
+   * remove any callback using the given listener function and associated with
+   * the given AlignFrame (or null for all callbacks);
+   * 
+   * @param listener
+   *          (may be null);
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void removeSelectionListener(String listener, AlignFrame af);
+
+  /**
+   * adjust horizontal/vertical scroll to make the given location the top left
+   * hand corner for the given view
+   * 
+   * @param topRow
+   *          -1 for current top row
+   * @param leftHandColumn
+   *          -1 for current left-hand column
+   * @param alf
+   *          alignment frame (default current)
+   */
+  public void scrollViewTo(int topRow, int leftHandColumn, AlignFrame alf);
+  /**
+   * select regions of the given alignment frame
+   * 
+   * @param alf
+   *          alignment frame (default current)
+   * @param toselect
+   *          String separated list { column range, seq1...seqn sequence ids }
+   * @param sep
+   *          separator between toselect fields
+   */
+  public void select(String[] sequenceIds, String[] columns,
+          AlignFrame alf);
+  
+  /**
+   * Set the state (visible or not) of the selected feature groups.
+   * 
+   * @param groups
+   * @param state
+   * @param alf
+   *          (default current)
+   */
+  public void setFeatureGroupState(String[] groups, boolean state,
+          AlignFrame alf);
+
+  /**
+   * Register a JavaScript function to handle alignment selection events. Events
+   * are generated when the user completes a selection event, or when the user
+   * deselects all selected regions. The method is called back with the
+   * following arguments:
+   * 
+   * the appletID (such as "Jalview1")
+   * 
+   * the source alignment frame
+   * 
+   * the SelectionSource object (for example, an AlignViewport)
+   * 
+   * the sequence set ID
+   * 
+   * an array of sequence IDs
+   * 
+   * an array of columns (single number or hyphenated range)
+   * 
+   * @param listener
+   *          name of JavaScript function to be called
+   * 
+   * @param alf
+   *          alignment frame (default ALL)
+   */
+  public void setSelectionListener(String listener, AlignFrame alf);
+
+  public void showOverview();
+
+  /**
+   * 
+   * @param pdbID
+   * @param fileType
+   * @param alf
+   *          align frame (default current)
+   */
+  public void showStructure(String pdbID, String fileType, AlignFrame alf);
+
+}
index e1bfbde..311f1e7 100644 (file)
@@ -773,8 +773,7 @@ public class APopupMenu extends java.awt.PopupMenu
 
           ap.alignFrame.addHistoryItem(editCommand);
 
-          ap.av.firePropertyChange("alignment", null,
-                  ap.av.getAlignment().getSequences());
+          ap.av.notifyAlignment();
         }
       }
     }
@@ -811,8 +810,7 @@ public class APopupMenu extends java.awt.PopupMenu
 
         ap.alignFrame.addHistoryItem(caseCommand);
 
-        ap.av.firePropertyChange("alignment", null,
-                ap.av.getAlignment().getSequences());
+        ap.av.notifyAlignment();
 
       }
     }
index c6e5032..063e949 100644 (file)
@@ -883,7 +883,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     else if (source == autoCalculate)
     {
-      viewport.autoCalculateConsensus = autoCalculate.getState();
+      viewport.setAutoCalculateConsensusAndConservation(autoCalculate.getState());
     }
     else if (source == sortByTree)
     {
@@ -1710,8 +1710,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
                                           // viewport.getColumnSelection().getHiddenColumns()
                                           // != null;
     updateEditMenuBar();
-    originalSource.firePropertyChange("alignment", null,
-            originalSource.getAlignment().getSequences());
+    originalSource.notifyAlignment();
   }
 
   /**
@@ -1743,8 +1742,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
                                           // != null;
 
     updateEditMenuBar();
-    originalSource.firePropertyChange("alignment", null,
-            originalSource.getAlignment().getSequences());
+    originalSource.notifyAlignment();
   }
 
   AlignmentViewport getOriginatingSource(CommandI command)
@@ -2093,8 +2091,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.getRanges().setEndSeq(viewport.getAlignment().getHeight() - 1); // BH
                                                                              // 2019.04.18
     viewport.getAlignment().getWidth();
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -2168,8 +2165,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.setSelectionGroup(null);
     viewport.getAlignment().deleteGroup(sg);
 
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
 
     if (viewport.getAlignment().getHeight() < 1)
     {
@@ -2381,7 +2377,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
         }
       }
 
-      viewport.firePropertyChange("alignment", null, al.getSequences());
+      viewport.notifyAlignment();
     }
   }
 
@@ -2425,7 +2421,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     // if (viewport.hasHiddenColumns)
     // viewport.getColumnSelection().compensateForEdits(shifts);
     ranges.setStartRes(seq.findIndex(startRes) - 1);
-    viewport.firePropertyChange("alignment", null, al.getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -2459,7 +2455,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     ranges.setStartRes(seq.findIndex(startRes) - 1);
 
-    viewport.firePropertyChange("alignment", null, al.getSequences());
+    viewport.notifyAlignment();
 
   }
 
index 97eb0c0..e2eebfc 100644 (file)
@@ -340,7 +340,7 @@ public class AlignViewport extends AlignmentViewport
     if (mappedCommand != null)
     {
       mappedCommand.doCommand(null);
-      firePropertyChange("alignment", null, getAlignment().getSequences());
+      notifyAlignment();
 
       // ap.scalePanelHolder.repaint();
       // ap.repaint();
index af951f2..aa08e79 100755 (executable)
@@ -28,6 +28,7 @@ import jalview.renderer.AwtRenderPanelI;
 import jalview.schemes.ResidueProperties;
 import jalview.util.Comparison;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.viewmodel.ViewportListenerI;
 import jalview.viewmodel.ViewportRanges;
 
@@ -50,6 +51,17 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.beans.PropertyChangeEvent;
 
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.AnnotationRenderer;
+import jalview.renderer.AwtRenderPanelI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
+import jalview.util.MessageManager;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+
 public class AnnotationPanel extends Panel
         implements AwtRenderPanelI, AdjustmentListener, ActionListener,
         MouseListener, MouseMotionListener, ViewportListenerI
index bd36b0d..6488c61 100644 (file)
@@ -227,8 +227,7 @@ public class RedundancyPanel extends SliderPanel
       ap.alignFrame.addHistoryItem(cut);
 
       PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
     }
 
   }
@@ -243,8 +242,7 @@ public class RedundancyPanel extends SliderPanel
     {
       ap.av.getHistoryList().remove(command);
       ap.alignFrame.updateEditMenuBar();
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
     }
 
     ap.paintAlignment(true, true);
index 776e9ad..a1e2340 100644 (file)
@@ -132,8 +132,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     if (editCommand != null && editCommand.getSize() > 0)
     {
       ap.alignFrame.addHistoryItem(editCommand);
-      av.firePropertyChange("alignment", null,
-              av.getAlignment().getSequences());
+      av.notifyAlignment();
     }
 
     startseq = -1;
index 5d3ab94..8c72f64 100644 (file)
@@ -23,7 +23,8 @@ package jalview.appletgui;
 import java.awt.Graphics;
 import java.awt.Insets;
 import java.awt.Panel;
-
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
 
 public class TitledPanel extends Panel
 {
index cb26fb5..46d6c11 100755 (executable)
@@ -715,8 +715,10 @@ public class TreeCanvas extends Panel
     ap.updateAnnotation();
     if (av.getCodingComplement() != null)
     {
-      ((AlignmentViewport) av.getCodingComplement()).firePropertyChange(
-              "alignment", null, ap.av.getAlignment().getSequences());
+      ((AlignmentViewport) av.getCodingComplement()).notifyAlignment();
+      // Technically, the property change is not the same because av is not necessarily getCodingComplement(), 
+      // but this is the appletgui, so I am not going to worry about it. BH
+      //.firePropertyChange("alignment", null, ap.av.getAlignment().getSequences());
     }
   }
 
similarity index 99%
rename from src/jalview/javascript/JSFunctionExec.java
rename to src/jalview/appletgui/js/JSFunctionExec.java
index 29f3fa9..247552b 100644 (file)
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.bin.JalviewLite;
 
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.appletgui.AlignFrame;
 
@@ -455,7 +455,7 @@ public interface JalviewLiteJsApi
    *          - separator separated list of PDB file URIs that this viewer is
    *          handling. These files must be in the same order they appear in
    *          Jmol (e.g. first one is frame 1, second is frame 2, etc).
-   * @see jalview.javascript.MouseOverStructureListener
+   * @see jalview.appletgui.js.MouseOverStructureListener
    */
   public abstract void setStructureListener(String listener,
           String modelSet);
similarity index 97%
rename from src/jalview/javascript/JsCallBack.java
rename to src/jalview/appletgui/js/JsCallBack.java
index 76d6e8d..2fe4dac 100644 (file)
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 public interface JsCallBack
 {
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.appletgui.AlignFrame;
 import jalview.bin.JalviewLite;
@@ -18,7 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.appletgui.AlignFrame;
 import jalview.bin.JalviewLite;
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.javascript;
+package jalview.appletgui.js;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.appletgui.AlignFrame;
+import jalview.appletgui.js.JsCallBack;
 import jalview.bin.JalviewLite;
 import jalview.datamodel.SequenceI;
 import jalview.ext.jmol.JmolCommands;
diff --git a/src/jalview/bin/AppletParams.java b/src/jalview/bin/AppletParams.java
new file mode 100644 (file)
index 0000000..726734d
--- /dev/null
@@ -0,0 +1,479 @@
+
+package jalview.bin;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jalview.gui.Preferences;
+
+/**
+ * Collection of all known applet tags from JalviewLite.
+ * Three cases; can be one or more of these:
+ * 
+ * CASE I. args[] name and value for ArgsParser
+ * 
+ * CASE II. applet parameter for JalviewJSApp
+ * 
+ * CASE III. mapped to a Preference
+ * 
+ * 
+ * @author hansonr
+ *
+ */
+@SuppressWarnings("serial")
+public class AppletParams extends HashMap<String, Object>
+{
+
+  private final static String[] params = { 
+      "alignpdbfiles",
+      "ANNOTATIONCOLOUR_MAX", "ANNOTATIONCOLOUR_MIN",
+      "annotations",
+      "APPLICATION_URL", "automaticScrolling", "centrecolumnlabels",
+      "debug", "defaultColour", "defaultColourNuc", "defaultColourProt",
+      "embedded", "enableSplitFrame", "externalstructureviewer", "features",
+      "file", "file2", "format", "heightScale", "hidefeaturegroups",
+      "jalviewhelpurl", "jnetfile", "jpredfile", "label", "linkLabel_",
+      "linkLabel_1", "linkURL_", "nojmol", "normaliseLogo",
+      "normaliseSequenceLogo", "oninit", "PDBFILE", "PDBSEQ",
+      "relaxedidmatch", "resolvetocodebase", "RGB", "scaleProteinAsCdna",
+      "scoreFile", "separator", "sequence", "showAnnotation", "showbutton",
+      "showConsensus", "showConsensusHistogram", "showConservation",
+      "showfeaturegroups", "showFeatureSettings", "showFullId",
+      "showGroupConsensus", "showGroupConservation", "showOccupancy",
+      "showQuality", "showSequenceLogo", "showTreeBootstraps",
+      "showTreeDistances", "showUnconserved", "showUnlinkedTreeNodes",
+      "sortBy", "sortByTree", "tree", "treeFile", "upperCase",
+      "userDefinedColour", "widthScale", "windowHeight", "windowWidth",
+      "wrap", };
+
+  public String getParam(String param, String def)
+  {
+    Object val = get(param);
+    return (val != null ? val.toString() : def);
+  }
+
+  // <applet
+  // code="jalview.bin.JalviewLite" width="140" height="35"
+  // archive="jalviewApplet.jar,JmolApplet-14.6.4_2016.10.26.jar,java-json.jar,json_simple-1.1.jar">
+  // <param name="permissions" value="sandbox"/>
+  // <param name="file" value="uniref50.fa"/>
+  // <param name="treeFile" value="ferredoxin.nw"/>
+  // <param name="userDefinedColour" value="C=yellow; R,K,H=FF5555;
+  // D,E=5555FF"/>
+  // <param name="sortByTree" value="True"/>
+  // <param name="showSequenceLogo" value="true"/>
+  // <param name="showGroupConsensus" value="true"/>
+  // <param name="showFullId" value="false"/>
+  // <param name="linkLabel_1" value="Uniprot"/>
+  // <param name="linkUrl_1"
+  // value="http://www.uniprot.org/uniprot/$SEQUENCE_ID$"/>
+  // <param name="linkLabel_2" value="EMBL-EBI Search"/>
+  // <param name="linkUrl_2"
+  // value="http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$"/>
+  // <param name="APPLICATION_URL"
+  // value="http://www.jalview.org/services/launchApp"/>
+  // </applet>
+  //
+  public AppletParams(String outerHTML)
+  {
+    String[] tokens = outerHTML.split("<param");
+    outerHTML = tokens[0];
+    String code = getAttr(outerHTML, "code");
+    if (!code.equals("jalview.bin.JalviewLite"))
+    {
+      return;
+    }
+    for (int i = tokens.length; --i > 0;)
+    {
+      String param = tokens[i];
+      String key = getAttr(param, "name");
+      if (key != null)
+      {
+        String value = getAttr(param, "value");
+        System.out.println("AppletParams " + key + " = \"" + value + "\"");
+        put(key, value);
+      }
+    }
+    put("_width", getAttr(outerHTML, "width"));
+    put("_height", getAttr(outerHTML, "height"));
+    put("_id", getAttr(outerHTML, "id"));
+    put("_name", getAttr(outerHTML, "name"));
+    put("_archive", getAttr(outerHTML, "archive"));
+    put("_code", code);
+  }
+
+  public AppletParams()
+  {
+  }
+
+  public static AppletParams getAppletParams(Map<String, Object> map,
+          List<String> vargs)
+  {
+    AppletParams appletParams = new AppletParams();
+    String resourcePath = getString(map, "resourcePath");
+    if (resourcePath == null)
+      resourcePath = "";
+    if (resourcePath.length() > 0 && !resourcePath.endsWith("/"))
+    {
+      resourcePath += "/";
+    }
+    for (int i = params.length; --i >= 0;)
+    {
+      String prefName = params[i];
+      Object value = map.get(prefName);
+      if (value != null)
+        addParam(vargs, prefName, value, appletParams, resourcePath);
+    }
+    return appletParams;
+  }
+
+  private static String getString(Map<String, Object> map, String key)
+  {
+    Object o = map.get(key);  
+    return (o == null ? null : o.toString());
+  }
+
+  public static AppletParams getAppletParams(String[] args,
+          List<String> vargs)
+  {
+    AppletParams appletParams = new AppletParams();
+    String resourcePath = "";
+    for (int i = args.length; --i > 0;) // > 0 is correct, not >=0
+    {
+      if (args[i].startsWith("name=\"Info.resourcePath\""))
+      {
+        resourcePath = getAttr(args[i], "value");
+        if (resourcePath.length() > 0 && !resourcePath.endsWith("/"))
+        {
+          resourcePath += "/";
+        }
+        break;
+      }
+    }
+    for (int i = 1; i < args.length; i++)
+    {
+      String arg = args[i].trim();
+      if (arg.startsWith("name="))
+      {
+        String prefName = getAttr(arg, "name");
+        String value = getAttr(arg, "value");
+        addParam(vargs, prefName, value, appletParams, resourcePath);
+      }
+    }
+    return appletParams;
+  }
+
+  private static void addParam(List<String> vargs, String prefName,
+          Object value, AppletParams appletParams, String resourcePath)
+  {
+
+    // note that Application arguments ARE case-sensitive, but
+    // Applet.getParameter() is not.
+
+    // prefName // CASE III
+
+    String argName = null;  // CASE I                
+
+    String appletName = prefName.toLowerCase();   // CASE II
+
+    // by nulling one or more of these names, that route will not be used.
+    
+    switch (appletName)
+    {
+
+    case "file":
+      argName = "open";
+      prefName = null;
+      value = resourcePath + value;
+      break;
+    case "file2":
+      argName = "open2";
+      prefName = null;
+      value = resourcePath + value;
+      break;
+    case "oninit":
+    case "hidefeaturegroups":
+      // applet parameter only
+      // setting argName to null indicates that we want
+      // JalviewJSApp to take care of this using getParameter or getParameterAsObject      
+     prefName = argName = null;
+      break;
+    case "tree":
+    case "treefile":
+      // setting appletName to null indicates that we want
+      // Jalview.doMain to taken care of this as Jalview args
+      argName = "tree";
+      prefName = null;
+      value = resourcePath + value;
+      break;
+
+    case "features":
+    case "jnetfile":
+    case "jpredfile":
+    case "pdbfile":
+    case "scorefile":
+    case "sequence":
+    case "annotations":
+      prefName = argName = null;
+      value = resourcePath + value;
+      break;
+
+      // non-loading preferences
+
+    case "defaultcolour":
+      prefName = Preferences.DEFAULT_COLOUR;
+      break;
+    case "defaultcolournuc":
+      prefName = Preferences.DEFAULT_COLOUR_NUC;
+      break;
+    case "defaultcolourprot":
+      prefName = Preferences.DEFAULT_COLOUR_PROT;
+      break;
+    case "annotationcolour_max":
+      prefName = Preferences.ANNOTATIONCOLOUR_MAX;
+      break;
+    case "annotationcolour_min":
+      prefName = Preferences.ANNOTATIONCOLOUR_MIN;
+      break;
+    case "enablesplitframe":
+      prefName = Preferences.ENABLE_SPLIT_FRAME;
+      break;
+    case "centrecolumnlabels":
+      prefName = Preferences.CENTRE_COLUMN_LABELS;
+      break;
+    case "sortby":
+      prefName = Preferences.SORT_ALIGNMENT; // id, etc.
+      break;
+    case "normalisesequencelogo":
+      prefName = Preferences.NORMALISE_CONSENSUS_LOGO;
+      break;
+    case "relaxedidmatch":
+      prefName = Preferences.RELAXEDSEQIDMATCHING;
+      break;
+    case "scaleproteinascdna":
+      prefName = Preferences.SCALE_PROTEIN_TO_CDNA;
+      break;
+    case "userdefinedcolour":
+      argName = "colour";
+      prefName = Preferences.USER_DEFINED_COLOURS;
+      break;
+    case "wrap":
+      prefName = Preferences.WRAP_ALIGNMENT;
+      break;
+    case "sortbytree":
+      argName = prefName;
+      prefName = Preferences.SORT_BY_TREE;
+      value = checkTF(value);
+      break;
+
+    // implemented; not tested:
+
+    case "pdbseq":
+    case "alignpdbfiles":
+      prefName = null;
+      break;
+    case "format":
+      argName = prefName;
+      break;
+    case "separator":
+      argName = prefName;
+      break;
+
+    // TODO: probably not relevant?
+
+    case "rgb":
+      prefName = null; // TODO no background for application?
+      break;
+    case "externalstructureviewer":
+      break;
+    case "application_url":
+      break;
+    case "automaticscrolling":
+      break;
+    case "heightscale":
+      break;
+    case "jalviewhelpurl":
+      break;
+    case "label":
+      break;
+    case "linklabel_":
+      prefName = "linkLabel_";
+      break;
+    case "linklabel_1":
+      prefName = "linkLabel_1";
+      break;
+    case "linkurl_":
+      prefName = "linkURL_";
+      break;
+
+    // unknown:
+
+    case "nojmol":
+    case "normaliselogo":
+    case "resolvetocodebase":
+    case "uppercase":
+    case "widthscale":
+    case "windowheight":
+    case "windowwidth":
+      prefName = null;
+      break;
+
+    // TRUE/FALSE
+
+    case "debug":
+    case "embedded":
+    case "showbutton":
+      value = checkTF(value);
+      break;
+    case "showannotation":
+      prefName = Preferences.SHOW_ANNOTATIONS;
+      value = checkTF(value);
+      break;
+    case "showconsensus":
+      prefName = Preferences.SHOW_CONSENSUS_LOGO;
+      value = checkTF(value);
+      break;
+    case "showconsensushistogram":
+      prefName = Preferences.SHOW_CONSENSUS_HISTOGRAM;
+      value = checkTF(value);
+      break;
+    case "showconservation":
+      prefName = Preferences.SHOW_CONSERVATION;
+      value = checkTF(value);
+      break;
+    case "showgroupconsensus":
+      prefName = Preferences.SHOW_GROUP_CONSENSUS;
+      value = checkTF(value);
+      break;
+    case "showgroupconservation":
+      prefName = Preferences.SHOW_GROUP_CONSERVATION;
+      value = checkTF(value);
+      break;
+    case "showoccupancy":
+      prefName = Preferences.SHOW_OCCUPANCY;
+      value = checkTF(value);
+      break;
+    case "showquality":
+      prefName = Preferences.SHOW_QUALITY;
+      value = checkTF(value);
+      break;
+    case "showsequencelogo":
+      prefName = Preferences.SHOW_CONSENSUS_LOGO;
+      value = checkTF(value);
+      break;
+    case "showunconserved":
+      prefName = Preferences.SHOW_UNCONSERVED;
+      value = checkTF(value);
+      break;
+    case "showfeaturegroups":
+    case "showfeaturesettings":
+    case "showfullid":
+    case "showtreebootstraps":
+    case "showtreedistances":
+    case "showunlinkedtreenodes":
+      value = checkTF(value);
+      break;
+    default:
+      if (appletName.startsWith("pdbfile")
+              || appletName.startsWith("sequence") && Character
+                      .isDigit(appletName.charAt(appletName.length() - 1)))
+      {
+        // could be pdbFile2, for example
+        prefName = argName = null;
+        value = resourcePath + value;
+        break;
+      }
+      // or one of the app preference names
+      break;
+    }
+
+    // CASE I.  args[] name and value for ArgsParser
+    //
+    // If given an argument name, 
+    // put name and value into application args
+    if (value != null && argName != null)
+    {
+      vargs.add(argName);
+      if (value != "true")
+      {
+        vargs.add(value.toString());
+      }
+    }
+    
+    // CASE II.   applet parameter for JalviewJSApp
+    
+    if (value == null)
+    {
+      value = "false";
+    }
+    System.out.println("AppletParams propName=" + prefName + " argName="
+            + argName + " appletName=" + appletName + " value=" + value);    
+    if (appletName != null)
+    {
+      appletParams.put(appletName, value);
+    }
+    
+    // CASE III.  mapped to a Preference
+    
+    if (prefName != null)
+    {
+      Cache.setPropertyNoSave(prefName, value.toString());
+    }
+  }
+
+  /**
+   * Check for a single-argument option.
+   * 
+   * @param value
+   * @return "true" or null
+   */
+  private static String checkTF(Object value)
+  {
+    return (("" + value).toLowerCase() == "true" ? "true" : null);
+  }
+
+  /**
+   * Crude applet innerHTML parser
+   * 
+   * @param tag
+   * @param attr
+   * @return
+   */
+  private static String getAttr(String tag, String attr)
+  {
+    int pt = tag.indexOf(attr + "=\"");
+    if (pt < 0)
+    {
+      System.out
+              .println("AppletParams did not read " + attr + " in " + tag);
+      return null;
+    }
+    // <param name="sortByTree" value="True"/>
+    int pt1 = pt + attr.length() + 2;
+    int pt2 = tag.indexOf("\"", pt1);
+    return (pt < 0 ? null : tag.substring(pt1, pt2));
+  }
+
+  public static void main(String[] args)
+  {
+    new AppletParams("<applet\r\n"
+            + "    code=\"jalview.bin.JalviewLite\" width=\"140\" height=\"35\"\r\n"
+            + "    archive=\"jalviewApplet.jar,JmolApplet-14.6.4_2016.10.26.jar,java-json.jar,json_simple-1.1.jar\">  \r\n"
+            + "  <param name=\"permissions\" value=\"sandbox\"/>\r\n"
+            + "  <param name=\"file\" value=\"uniref50.fa\"/>\r\n"
+            + "  <param name=\"treeFile\" value=\"ferredoxin.nw\"/>\r\n"
+            + "  <param name=\"userDefinedColour\" value=\"C=yellow; R,K,H=FF5555; D,E=5555FF\"/>\r\n"
+            + "  <param name=\"sortByTree\" value=\"True\"/>\r\n"
+            + "  <param name=\"showSequenceLogo\" value=\"true\"/>\r\n"
+            + "  <param name=\"showGroupConsensus\" value=\"true\"/>\r\n"
+            + "  <param name=\"showFullId\" value=\"false\"/>\r\n"
+            + "    <param name=\"linkLabel_1\" value=\"Uniprot\"/>\r\n"
+            + "    <param name=\"linkUrl_1\" value=\"http://www.uniprot.org/uniprot/$SEQUENCE_ID$\"/>\r\n"
+            + "    <param name=\"linkLabel_2\" value=\"EMBL-EBI Search\"/>\r\n"
+            + "    <param name=\"linkUrl_2\" value=\"http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$\"/>\r\n"
+            + "    <param name=\"APPLICATION_URL\" value=\"http://www.jalview.org/services/launchApp\"/>\r\n"
+            + "     </applet>");
+  }
+
+}
\ No newline at end of file
diff --git a/src/jalview/bin/ApplicationSingletonProvider.java b/src/jalview/bin/ApplicationSingletonProvider.java
new file mode 100644 (file)
index 0000000..b64f40c
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview 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 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview 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 Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import jalview.util.Platform;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class to hold singleton objects, whose scope (context) is
+ * <ul>
+ * <li>the Java runtime (JVM) when running as Java</li>
+ * <li>one 'applet', when running as JalviewJS</li>
+ * </ul>
+ * This allows separation of multiple JS applets running on the same browser
+ * page, each with their own 'singleton' instances.
+ * <p>
+ * Instance objects are held in a separate Map (keyed by Class) for each
+ * context. For Java, this is just a single static Map. For SwingJS, the map is
+ * stored as a field {@code _swingjsSingletons} of
+ * {@code Thread.currentThread.getThreadGroup()}, as a proxy for the applet.
+ * <p>
+ * Note that when an applet is stopped, its ThreadGroup is removed, allowing any
+ * singleton references to be garbage collected.
+ * 
+ * @author hansonr
+ */
+public class ApplicationSingletonProvider
+{
+  /**
+   * A tagging interface to mark classes whose singleton instances may be served
+   * by {@code ApplicationSingletonProvider}, giving a distinct instance per JS
+   * 'applet'.
+   * <p>
+   * A class whose singleton should have global scope (be shared across all
+   * applets on a page) should <em>not</em> use this mechanism, but just provide
+   * a single instance (class static member) in the normal way.
+   */
+  public interface ApplicationSingletonI
+  {
+  }
+  
+  /*
+   * Map used to hold singletons in JVM context
+   */
+  private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> singletons = new HashMap<>();
+
+  /**
+   * private constructor for non-instantiable class
+   */
+  private ApplicationSingletonProvider()
+  {
+  }
+
+  /**
+   * Returns the singletons map for the current context (JVM for Java,
+   * ThreadGroup for JS), creating the map on the first request for each JS
+   * ThreadGroup
+   * 
+   * @return
+   */
+  private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> getContextMap()
+  {
+    @SuppressWarnings("unused")
+    ThreadGroup g = (Platform.isJS()
+            ? Thread.currentThread().getThreadGroup()
+            : null);
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = singletons;
+    /** @j2sNative map = g._swingjsSingletons; */
+    if (map == null)
+    {
+      map = new HashMap<>();
+      /** @j2sNative g._swingjsSingletons = map; */
+    }
+
+    return map;
+  }
+
+  /**
+   * Answers the singleton instance of the given class for the current context
+   * (JVM or SwingJS 'applet'). If no instance yet exists, one is created, by
+   * calling the class's no-argument constructor. Answers null if any error
+   * occurs (or occurred previously for the same class).
+   * 
+   * @param c
+   * @return
+   */
+  public static ApplicationSingletonI getInstance(Class<? extends ApplicationSingletonI> c)
+  {
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
+    if (map.containsKey(c))
+    {
+      /*
+       * singleton already created _or_ creation failed (null value stored)
+       */
+      return map.get(c);
+    }
+
+    /*
+     * create and save the singleton
+     */
+    ApplicationSingletonI o = map.get(c);
+    try
+    {
+      Constructor<? extends ApplicationSingletonI> con = c
+              .getDeclaredConstructor();
+      con.setAccessible(true);
+      o = con.newInstance();
+    } catch (IllegalAccessException | InstantiationException
+            | IllegalArgumentException | InvocationTargetException
+            | NoSuchMethodException | SecurityException e)
+    {
+      Cache.log.error("Failed to create singleton for " + c.toString()
+              + ", error was: " + e.toString());
+      e.printStackTrace();
+    }
+
+    /*
+     * store the new singleton; note that a
+     * null value is saved if construction failed
+     */
+    getContextMap().put(c, o);
+    return o;
+  }
+
+  /**
+   * Removes the current singleton instance of the given class from the current
+   * application context. This has the effect of ensuring that a new instance is
+   * created the next time one is requested.
+   * 
+   * @param c
+   */
+  public static void removeInstance(
+          Class<? extends ApplicationSingletonI> c)
+  {
+    Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
+    if (map != null)
+    {
+      map.remove(c);
+    }
+  }
+}
index c927f1f..c5c08f6 100644 (file)
  */
 package jalview.bin;
 
+import jalview.util.Platform;
+
 import java.net.URLDecoder;
-import java.util.Vector;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Notes: this argParser does not distinguish between parameter switches,
@@ -34,19 +37,105 @@ import java.util.Vector;
  */
 public class ArgsParser
 {
-  Vector<String> vargs = null;
+
+  // BH 2019 - new
+
+  public static final String NOCALCULATION = "nocalculation";
+
+  public static final String NOMENUBAR = "nomenubar";
+
+  public static final String NOSTATUS = "nostatus";
+
+  public static final String SHOWOVERVIEW = "showoverview";
+
+  //
+  public static final String ANNOTATIONS = "annotations";
+
+  public static final String COLOUR = "colour";
+
+  public static final String FEATURES = "features";
+
+  public static final String GROOVY = "groovy";
+
+  public static final String GROUPS = "groups";
+
+  public static final String HEADLESS = "headless";
+
+  public static final String JABAWS = "jabaws";
+
+  public static final String NOANNOTATION = "no-annotation";
+
+  public static final String NOANNOTATION2 = "noannotation"; // BH 2019.05.07
+
+  public static final String NODISPLAY = "nodisplay";
+
+  public static final String NOGUI = "nogui";
+
+  public static final String NONEWS = "nonews";
+
+  public static final String NOQUESTIONNAIRE = "noquestionnaire";
+
+  public static final String NOSORTBYTREE = "nosortbytree";
+
+  public static final String NOUSAGESTATS = "nousagestats";
+
+  public static final String OPEN = "open";
+
+  public static final String OPEN2 = "open2"; // BH added -- for applet
+                                              // compatibility; not fully
+                                              // implemented
+
+  public static final String PROPS = "props";
+
+  public static final String QUESTIONNAIRE = "questionnaire";
+
+  public static final String SETPROP = "setprop";
+
+  public static final String SORTBYTREE = "sortbytree";
+
+  public static final String TREE = "tree";
+
+  public static final String VDOC = "vdoc";
+
+  public static final String VSESS = "vsess";
+
+  private List<String> vargs = null;
+
+  private boolean isApplet;
+
+  private AppletParams appletParams;
+
+  public boolean isApplet()
+  {
+    return isApplet;
+  }
 
   public ArgsParser(String[] args)
   {
-    vargs = new Vector<String>();
-    for (int i = 0; i < args.length; i++)
+    vargs = new ArrayList<>();
+    isApplet = (args.length > 0 && args[0].startsWith("<applet"));
+    if (isApplet)
     {
-      String arg = args[i].trim();
-      if (arg.charAt(0) == '-')
+      appletParams = AppletParams.getAppletParams(args, vargs);
+    }
+    else
+    {
+      if (Platform.isJS())
+
       {
-        arg = arg.substring(1);
+        isApplet = true;
+        appletParams = AppletParams
+                .getAppletParams(Platform.getAppletInfoAsMap(), vargs);
+      }
+      for (int i = 0; i < args.length; i++)
+      {
+        String arg = args[i].trim();
+        if (arg.charAt(0) == '-')
+        {
+          arg = arg.substring(1);
+        }
+        vargs.add(arg);
       }
-      vargs.addElement(arg);
     }
   }
 
@@ -67,9 +156,9 @@ public class ArgsParser
     String dc = null, ret = null;
     if (index != -1)
     {
-      ret = vargs.elementAt(index + 1).toString();
-      vargs.removeElementAt(index);
-      vargs.removeElementAt(index);
+      ret = vargs.get(index + 1).toString();
+      vargs.remove(index);
+      vargs.remove(index);
       if (utf8decode && ret != null)
       {
         try
@@ -95,7 +184,7 @@ public class ArgsParser
   {
     if (vargs.contains(arg))
     {
-      vargs.removeElement(arg);
+      vargs.remove(arg);
       return true;
     }
     else
@@ -114,4 +203,13 @@ public class ArgsParser
     return vargs.size();
   }
 
+  public Object getAppletValue(String key, String def, boolean asString)
+  {
+    Object value;
+    return (appletParams == null ? null
+            : (value = appletParams.get(key.toLowerCase())) == null
+                    ? def : asString ? "" + value
+                    : value);
+  }
+
 }
index 788cb02..09004f4 100755 (executable)
  */
 package jalview.bin;
 
-import jalview.datamodel.PDBEntry;
-import jalview.gui.UserDefinedColours;
-import jalview.schemes.ColourSchemeLoader;
-import jalview.schemes.ColourSchemes;
-import jalview.schemes.UserColourScheme;
-import jalview.structure.StructureImportSettings;
-import jalview.urls.IdOrgSettings;
-import jalview.util.ColorUtils;
-import jalview.util.Platform;
-import jalview.ws.sifts.SiftsSettings;
-
 import java.awt.Color;
 import java.io.BufferedReader;
-import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.InputStream;
@@ -54,6 +42,18 @@ import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 import org.apache.log4j.SimpleLayout;
 
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+import jalview.datamodel.PDBEntry;
+import jalview.gui.UserDefinedColours;
+import jalview.schemes.ColourSchemeLoader;
+import jalview.schemes.ColourSchemes;
+import jalview.schemes.UserColourScheme;
+import jalview.structure.StructureImportSettings;
+import jalview.urls.IdOrgSettings;
+import jalview.util.ColorUtils;
+import jalview.util.Platform;
+import jalview.ws.sifts.SiftsSettings;
+
 /**
  * Stores and retrieves Jalview Application Properties Lists and fields within
  * list entries are separated by '|' symbols unless otherwise stated (|) clauses
@@ -208,8 +208,26 @@ import org.apache.log4j.SimpleLayout;
  * @author $author$
  * @version $Revision$
  */
-public class Cache
+public class Cache implements ApplicationSingletonI
 {
+
+  private Cache()
+  {
+    // private singleton
+  }
+
+  /**
+   * In Java, this will be a static field instance, which will be
+   * application-specific; in JavaScript it will be an applet-specific instance
+   * tied to the applet's ThreadGroup.
+   * 
+   * @return
+   */
+  public static Cache getInstance()
+  {
+    return (Cache) ApplicationSingletonProvider.getInstance(Cache.class);
+  }
+
   /**
    * property giving log4j level for CASTOR loggers
    */
@@ -228,10 +246,8 @@ public class Cache
   /**
    * Sifts settings
    */
-  public static final String DEFAULT_SIFTS_DOWNLOAD_DIR = System
-          .getProperty("user.home") + File.separatorChar
-          + ".sifts_downloads" + File.separatorChar;
-
+  public static final String DEFAULT_SIFTS_DOWNLOAD_DIR = Platform.getUserPath(".sifts_downloads/");
+  
   private final static String DEFAULT_CACHE_THRESHOLD_IN_DAYS = "2";
 
   private final static String DEFAULT_FAIL_SAFE_PID_THRESHOLD = "30";
@@ -239,8 +255,7 @@ public class Cache
   /**
    * Identifiers.org download settings
    */
-  private static final String ID_ORG_FILE = System.getProperty("user.home")
-          + File.separatorChar + ".identifiers.org.ids.json";
+  private static final String ID_ORG_FILE = Platform.getUserPath(".identifiers.org.ids.json");
 
   /**
    * Allowed values are PDB or mmCIF
@@ -266,7 +281,7 @@ public class Cache
   public static Logger log;
 
   /** Jalview Properties */
-  public static Properties applicationProperties = new Properties()
+  private Properties applicationProperties = new Properties()
   {
     // override results in properties output in alphabetical order
     @Override
@@ -333,11 +348,18 @@ public class Cache
    */
   public static void loadProperties(String propsFile)
   {
+
+    getInstance().loadPropertiesImpl(propsFile);
+
+  }
+
+  private void loadPropertiesImpl(String propsFile)
+  {
+
     propertiesFile = propsFile;
     if (propsFile == null && !propsAreReadOnly)
     {
-      propertiesFile = System.getProperty("user.home") + File.separatorChar
-              + ".jalview_properties";
+      propertiesFile = Platform.getUserPath(".jalview_properties");
     }
     else
     {
@@ -415,9 +437,9 @@ public class Cache
     }
     if (authorDetails == null)
     {
-        applicationProperties.remove("AUTHORS");
-        applicationProperties.remove("AUTHORFNAMES");
-        applicationProperties.remove("YEAR");
+      applicationProperties.remove("AUTHORS");
+      applicationProperties.remove("AUTHORFNAMES");
+      applicationProperties.remove("YEAR");
     }
 
     loadBuildProperties(false);
@@ -450,7 +472,8 @@ public class Cache
 
     String jnlpVersion = System.getProperty("jalview.version");
 
-    // jnlpVersion will be null if a latest version check for the channel needs to
+    // jnlpVersion will be null if a latest version check for the channel needs
+    // to
     // be done
     // Dont do this check if running in headless mode
 
@@ -543,8 +566,8 @@ public class Cache
         url = Cache.class.getResource(resourcePath).toString();
       } catch (Exception ex)
       {
-        System.err.println("Failed to resolve resource " + resourcePath + ": "
-                + ex.getMessage());
+        System.err.println("Failed to resolve resource " + resourcePath
+                + ": " + ex.getMessage());
       }
     }
     else
@@ -555,7 +578,7 @@ public class Cache
     return url;
   }
 
-  public static void loadBuildProperties(boolean reportVersion)
+  public void loadBuildProperties(boolean reportVersion)
   {
     String codeInstallation = getProperty("INSTALLATION");
     boolean printVersion = codeInstallation == null;
@@ -593,12 +616,12 @@ public class Cache
     new BuildDetails(codeVersion, null, codeInstallation);
     if (printVersion && reportVersion)
     {
-      System.out
-            .println("Jalview Version: " + codeVersion + codeInstallation);
+      System.out.println(
+              "Jalview Version: " + codeVersion + codeInstallation);
     }
   }
 
-  private static void deleteBuildProperties()
+  private void deleteBuildProperties()
   {
     applicationProperties.remove("LATEST_VERSION");
     applicationProperties.remove("VERSION");
@@ -620,12 +643,12 @@ public class Cache
    */
   public static String getProperty(String key)
   {
-    String prop = applicationProperties.getProperty(key);
-    if (prop == null && Platform.isJS())
-    {
-      prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
-              + "_" + JS_PROPERTY_PREFIX + key);
-    }
+    String prop = getInstance().applicationProperties.getProperty(key);
+    // if (prop == null && Platform.isJS())
+    // {
+    // prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
+    // + "_" + JS_PROPERTY_PREFIX + key);
+    // }
     return prop;
   }
 
@@ -685,33 +708,48 @@ public class Cache
    */
   public static Object setProperty(String key, String obj)
   {
-    Object oldValue = null;
-    try
-    {
-      oldValue = applicationProperties.setProperty(key, obj);
-      if (propertiesFile != null && !propsAreReadOnly)
-      {
-        FileOutputStream out = new FileOutputStream(propertiesFile);
-        applicationProperties.store(out, "---JalviewX Properties File---");
-        out.close();
-      }
-    } catch (Exception ex)
-    {
-      System.out.println(
-              "Error setting property: " + key + " " + obj + "\n" + ex);
-    }
-    return oldValue;
+    return getInstance().setPropertyImpl(key, obj, true);
+  }
+
+  /**
+   * Removes the specified property from the jalview properties file
+   * 
+   * @param key
+   */
+  public static void removeProperty(String key)
+  {
+    getInstance().removePropertyImpl(key, true);
+  }
+
+  /**
+   * Removes the named property for the running application, without saving the
+   * properties file
+   * 
+   * BH noting that ColourMenuHelper calls this. If the intent is to save, then
+   * simply chanet that call to removeProperty(key).
+   * 
+   * @param key
+   */
+  public static void removePropertyNoSave(String key)
+  {
+
+    getInstance().
+
+            removePropertyImpl(key, false);
   }
 
   /**
-   * remove the specified property from the jalview properties file
+   * Removes the named property, and optionally saves the current properties to
+   * file
    * 
-   * @param string
+   * @param key
+   * @param andSave
    */
-  public static void removeProperty(String string)
+  private void removePropertyImpl(String key, boolean andSave)
   {
-    applicationProperties.remove(string);
-    saveProperties();
+    applicationProperties.remove(key);
+    if (andSave)
+      saveProperties();
   }
 
   /**
@@ -719,6 +757,15 @@ public class Cache
    */
   public static void saveProperties()
   {
+    getInstance().savePropertiesImpl();
+  }
+
+  /**
+   * save the properties to the jalview properties path
+   */
+  private void savePropertiesImpl()
+
+  {
     if (!propsAreReadOnly)
     {
       try
@@ -970,9 +1017,9 @@ public class Cache
    * @param property
    * @param colour
    */
-  public static void setColourProperty(String property, Color colour)
+  public static void setColourPropertyNoSave(String property, Color colour)
   {
-    setProperty(property, jalview.util.Format.getHexString(colour));
+    setPropertyNoSave(property, jalview.util.Format.getHexString(colour));
   }
 
   /**
@@ -1000,11 +1047,16 @@ public class Cache
   public static Date getDateProperty(String propertyName)
   {
     String val = getProperty(propertyName);
+    
     if (val != null)
     {
       try
       {
-        return date_format.parse(val);
+        if ((val = val.trim()).indexOf(",") < 0 && val.indexOf("-") >= 0 && val.indexOf(" ") == val.lastIndexOf(" ")) {
+          val = val.replace(" ",", ").replace('-',' ');
+        }
+        Date date =  date_format.parse(val);
+        return date;
       } catch (Exception ex)
       {
         System.err.println("Invalid or corrupt date in property '"
@@ -1053,11 +1105,11 @@ public class Cache
     }
     if (value == null || value.trim().length() < 1)
     {
-      Cache.applicationProperties.remove(propName);
+      getInstance().applicationProperties.remove(propName);
     }
     else
     {
-      Cache.applicationProperties.setProperty(propName, value);
+      getInstance().applicationProperties.setProperty(propName, value);
     }
   }
 
@@ -1107,7 +1159,7 @@ public class Cache
       }
       else
       {
-        applicationProperties
+        getInstance().applicationProperties
                 .remove(UserDefinedColours.USER_DEFINED_COLOURS);
       }
     }
@@ -1138,16 +1190,20 @@ public class Cache
   public static String getVersionDetailsForConsole()
   {
     StringBuilder sb = new StringBuilder();
-    sb.append("Jalview Version: " + jalview.bin.Cache.getDefault("VERSION", "TEST"));
+    sb.append("Jalview Version: "
+            + jalview.bin.Cache.getDefault("VERSION", "TEST"));
     sb.append("\n");
     sb.append("Jalview Installation: "
             + jalview.bin.Cache.getDefault("INSTALLATION", "unknown"));
     sb.append("\n");
-    sb.append("Build Date: " + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
+    sb.append("Build Date: "
+            + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
     sb.append("\n");
     sb.append("Java version: " + System.getProperty("java.version"));
     sb.append("\n");
-    sb.append(System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version"));
+    sb.append(System.getProperty("os.arch") + " "
+            + System.getProperty("os.name") + " "
+            + System.getProperty("os.version"));
     sb.append("\n");
     appendIfNotNull(sb, "Install4j version: ",
             System.getProperty("sys.install4jVersion"), "\n", null);
@@ -1155,7 +1211,9 @@ public class Cache
             System.getProperty("installer_template_version"), "\n", null);
     appendIfNotNull(sb, "Launcher version: ",
             System.getProperty("launcher_version"), "\n", null);
-    if (jalview.bin.Cache.getDefault("VERSION", "TEST").equals("DEVELOPMENT")) {
+    if (jalview.bin.Cache.getDefault("VERSION", "TEST")
+            .equals("DEVELOPMENT"))
+    {
       appendIfNotNull(sb, "Getdown appdir: ",
               System.getProperty("getdownappdir"), "\n", null);
       appendIfNotNull(sb, "Java home: ", System.getProperty("java.home"),
@@ -1174,4 +1232,51 @@ public class Cache
     // eg 'built from Source' or update channel
     return jalview.bin.Cache.getDefault("INSTALLATION", "unknown");
   }
+
+  /**
+   * 
+   * For AppletParams and Preferences ok_actionPerformed and
+   * startupFileTextfield_mouseClicked
+   * 
+   * Sets a property value for the running application, without saving it to the
+   * properties file
+   * 
+   * @param key
+   * @param obj
+   */
+  public static void setPropertyNoSave(String key, String obj)
+  {
+    getInstance().setPropertyImpl(key, obj, false);
+  }
+
+  /**
+   * Sets a property value, and optionally also saves the current properties to
+   * file
+   * 
+   * @param key
+   * @param obj
+   * @param andSave
+   * @return
+   */
+  private Object setPropertyImpl(
+          String key, String obj, boolean andSave)
+  {
+    Object oldValue = null;
+    try
+    {
+      oldValue = applicationProperties.setProperty(key, obj);
+      if (andSave && !propsAreReadOnly && propertiesFile != null)
+      {
+        FileOutputStream out = new FileOutputStream(propertiesFile);
+        applicationProperties.store(out, "---JalviewX Properties File---");
+        out.close();
+      }
+    } catch (Exception ex)
+    {
+      System.out.println(
+              "Error setting property: " + key + " " + obj + "\n" + ex);
+    }
+    return oldValue;
+  }
+
 }
index 1b72940..75504be 100755 (executable)
  */
 package jalview.bin;
 
-import jalview.ext.so.SequenceOntology;
-import jalview.gui.AlignFrame;
-import jalview.gui.Desktop;
-import jalview.gui.PromptUserConfig;
-import jalview.io.AppletFormatAdapter;
-import jalview.io.BioJsHTMLOutput;
-import jalview.io.DataSourceType;
-import jalview.io.FileFormat;
-import jalview.io.FileFormatException;
-import jalview.io.FileFormatI;
-import jalview.io.FileLoader;
-import jalview.io.HtmlSvgOutput;
-import jalview.io.IdentifyFile;
-import jalview.io.NewickFile;
-import jalview.io.gff.SequenceOntologyFactory;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ColourSchemeProperty;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.jws2.Jws2Discoverer;
-
+import java.awt.GraphicsEnvironment;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -60,9 +40,6 @@ import java.security.Policy;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Vector;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 
 import javax.swing.LookAndFeel;
 import javax.swing.UIManager;
@@ -71,6 +48,31 @@ import com.threerings.getdown.util.LaunchUtil;
 
 import groovy.lang.Binding;
 import groovy.util.GroovyScriptEngine;
+import jalview.api.AlignCalcWorkerI;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+import jalview.ext.so.SequenceOntology;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.Desktop;
+import jalview.gui.Preferences;
+import jalview.gui.PromptUserConfig;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.BioJsHTMLOutput;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatException;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.FileLoader;
+import jalview.io.HtmlSvgOutput;
+import jalview.io.IdentifyFile;
+import jalview.io.NewickFile;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.jws2.Jws2Discoverer;
 
 /**
  * Main class for Jalview Application <br>
@@ -87,29 +89,74 @@ import groovy.util.GroovyScriptEngine;
  * @author $author$
  * @version $Revision$
  */
-public class Jalview
+public class Jalview implements ApplicationSingletonI
 {
-  static
+
+  // for testing those nasty messages you cannot ever find.
+  // static
+  // {
+  // System.setOut(new PrintStream(new ByteArrayOutputStream())
+  // {
+  // @Override
+  // public void println(Object o)
+  // {
+  // if (o != null)
+  // {
+  // System.err.println(o);
+  // }
+  // }
+  //
+  // });
+  // }
+  public static Jalview getInstance()
   {
-    Platform.getURLCommandArguments();
+    return (Jalview) ApplicationSingletonProvider
+            .getInstance(Jalview.class);
   }
 
-  // singleton instance of this class
+  private Jalview()
+  {
+  }
 
-  private static Jalview instance;
+  private boolean headless;
 
   private Desktop desktop;
 
-  public static AlignFrame currentAlignFrame;
+  public AlignFrame currentAlignFrame;
+
+  public String appletResourcePath;
+
+  public String j2sAppletID;
+
+  private boolean noCalculation, noMenuBar, noStatus;
+
+  private boolean noAnnotation;
+
+  public boolean getStartCalculations()
+  {
+    return !noCalculation;
+  }
+
+  public boolean getAllowMenuBar()
+  {
+    return !noMenuBar;
+  }
+
+  public boolean getShowStatus()
+  {
+    return !noStatus;
+  }
+
+  public boolean getShowAnnotation()
+  {
+    return !noAnnotation;
+  }
 
   static
   {
-    if (!Platform.isJS())
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
+    if (Platform.isJS()) {
+        Platform.getURLCommandArguments();
+    } else /** @j2sIgnore */
     {
       // grab all the rights we can for the JVM
       Policy.setPolicy(new Policy()
@@ -121,7 +168,7 @@ public class Jalview
           perms.add(new AllPermission());
           return (perms);
         }
-  
+
         @Override
         public void refresh()
         {
@@ -189,66 +236,22 @@ public class Jalview
 
   }
 
-  public static Jalview getInstance()
-  {
-    return instance;
-  }
+  private final static boolean doPlatformLogging = false;
 
   /**
    * main class for Jalview application
    * 
    * @param args
-   *               open <em>filename</em>
+   *          open <em>filename</em>
    */
   public static void main(String[] args)
   {
-//  setLogging(); // BH - for event debugging in JavaScript
-    instance = new Jalview();
-    instance.doMain(args);
-}
-
-  private static void logClass(String name) 
-  {  
-    // BH - for event debugging in JavaScript
-      ConsoleHandler consoleHandler = new ConsoleHandler();
-      consoleHandler.setLevel(Level.ALL);
-      Logger logger = Logger.getLogger(name);
-      logger.setLevel(Level.ALL);
-      logger.addHandler(consoleHandler);
-  }
-
-  @SuppressWarnings("unused")
-  private static void setLogging() 
-  {
-
-    /**
-     * @j2sIgnore
-     * 
-     */
+    if (doPlatformLogging)
     {
-      System.out.println("not in js");
+      Platform.startJavaLogging();
     }
-
-    // BH - for event debugging in JavaScript (Java mode only)
-    if (!Platform.isJS())
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
-    {
-      Logger.getLogger("").setLevel(Level.ALL);
-        logClass("java.awt.EventDispatchThread");
-        logClass("java.awt.EventQueue");
-        logClass("java.awt.Component");
-        logClass("java.awt.focus.Component");
-        logClass("java.awt.focus.DefaultKeyboardFocusManager"); 
-    }  
-
+    getInstance().doMain(args);
   }
-  
-
-  
 
   /**
    * @param args
@@ -256,71 +259,93 @@ public class Jalview
   void doMain(String[] args)
   {
 
-    if (!Platform.isJS())
+    boolean isJS = Platform.isJS();
+    if (!isJS)
     {
       System.setSecurityManager(null);
     }
 
     System.out
-            .println("Java version: "
-                    + System.getProperty("java.version"));
+            .println("Java version: " + System.getProperty("java.version"));
     System.out.println("Java Home: " + System.getProperty("java.home"));
     System.out.println(System.getProperty("os.arch") + " "
             + System.getProperty("os.name") + " "
             + System.getProperty("os.version"));
     String val = System.getProperty("sys.install4jVersion");
-    if (val != null) {
-    System.out.println("Install4j version: " + val);
+    if (val != null)
+    {
+      System.out.println("Install4j version: " + val);
     }
     val = System.getProperty("installer_template_version");
-    if (val != null) {
+    if (val != null)
+    {
       System.out.println("Install4j template version: " + val);
     }
     val = System.getProperty("launcher_version");
-    if (val != null) {
+    if (val != null)
+    {
       System.out.println("Launcher version: " + val);
     }
 
     // report Jalview version
-    Cache.loadBuildProperties(true);
+    Cache.getInstance().loadBuildProperties(true);
 
     ArgsParser aparser = new ArgsParser(args);
-    boolean headless = false;
+    headless = false;
 
     String usrPropsFile = aparser.getValue("props");
+
     Cache.loadProperties(usrPropsFile); // must do this before
-    if (usrPropsFile != null)
+
+    boolean allowServices = true;
+    
+    if (isJS)
     {
-      System.out.println(
-              "CMD [-props " + usrPropsFile + "] executed successfully!");
+      j2sAppletID = Platform.getAppID(null);
+      Preferences.setAppletDefaults();
+      Cache.loadProperties(usrPropsFile); // again, because we
+      // might be changing defaults here?
+      appletResourcePath = (String) aparser.getAppletValue("resourcepath",
+              null, true);
     }
-
-    if (!Platform.isJS())
+    else
     /**
      * Java only
      * 
      * @j2sIgnore
      */
     {
+
+      if (usrPropsFile != null)
+      {
+        System.out.println(
+                "CMD [-props " + usrPropsFile + "] executed successfully!");
+      }
       if (aparser.contains("help") || aparser.contains("h"))
       {
         showUsage();
         System.exit(0);
       }
+      // BH note: Only -nodisplay is official; others are deprecated?
       if (aparser.contains("nodisplay") || aparser.contains("nogui")
-              || aparser.contains("headless"))
+              || aparser.contains("headless")
+              || GraphicsEnvironment.isHeadless())
       {
-        System.setProperty("java.awt.headless", "true");
+        if (!isJS) {
+          // BH Definitely not a good idea in JavaScript; 
+          // probably should not be here for Java, either.  
+          System.setProperty("java.awt.headless", "true");
+        }
         headless = true;
       }
-      // anything else!
 
-      final String jabawsUrl = aparser.getValue("jabaws");
-      if (jabawsUrl != null)
+      final String jabawsUrl = aparser.getValue(ArgsParser.JABAWS);
+      allowServices = !("none".equals(jabawsUrl));
+      if (allowServices && jabawsUrl != null)
       {
         try
         {
-          Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl);
+          Jws2Discoverer.getInstance().setPreferredUrl(jabawsUrl);
           System.out.println(
                   "CMD [-jabaws " + jabawsUrl + "] executed successfully!");
         } catch (MalformedURLException e)
@@ -331,7 +356,7 @@ public class Jalview
       }
 
     }
-    String defs = aparser.getValue("setprop");
+    String defs = aparser.getValue(ArgsParser.SETPROP);
     while (defs != null)
     {
       int p = defs.indexOf('=');
@@ -342,18 +367,13 @@ public class Jalview
       else
       {
         System.out.println("Executing setprop argument: " + defs);
-        if (Platform.isJS())
+        if (isJS)
         {
-          Cache.setProperty(defs.substring(0,p), defs.substring(p+1));
+          Cache.setProperty(defs.substring(0, p), defs.substring(p + 1));
         }
       }
       defs = aparser.getValue("setprop");
     }
-    if (System.getProperty("java.awt.headless") != null
-            && System.getProperty("java.awt.headless").equals("true"))
-    {
-      headless = true;
-    }
     System.setProperty("http.agent",
             "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown"));
     try
@@ -367,69 +387,72 @@ public class Jalview
       System.exit(0);
     }
 
-    desktop = null;
-
-    try
-    {
-      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-    } catch (Exception ex)
+    if (!isJS)
+    /** @j2sIgnore */
     {
-      System.err.println("Unexpected Look and Feel Exception");
-      ex.printStackTrace();
-    }
-    if (Platform.isAMacAndNotJS())
-    {
-
-      LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
-              .getLookAndFeel();
-      System.setProperty("com.apple.mrj.application.apple.menu.about.name",
-              "Jalview");
-      System.setProperty("apple.laf.useScreenMenuBar", "true");
-      if (lookAndFeel != null)
+      try
       {
-        try
-        {
-          UIManager.setLookAndFeel(lookAndFeel);
-        } catch (Throwable e)
-        {
-          System.err.println(
-                  "Failed to set QuaQua look and feel: " + e.toString());
-        }
+        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+      } catch (Exception ex)
+      {
+        System.err.println("Unexpected Look and Feel Exception");
+        ex.printStackTrace();
       }
-      if (lookAndFeel == null
-              || !(lookAndFeel.getClass().isAssignableFrom(
-                      UIManager.getLookAndFeel().getClass()))
-              || !UIManager.getLookAndFeel().getClass().toString()
-                      .toLowerCase().contains("quaqua"))
+      if (Platform.isMac())
       {
-        try
+
+        LookAndFeel lookAndFeel = ch.randelshofer.quaqua.QuaquaManager
+                .getLookAndFeel();
+        System.setProperty(
+                "com.apple.mrj.application.apple.menu.about.name",
+                "Jalview");
+        System.setProperty("apple.laf.useScreenMenuBar", "true");
+        if (lookAndFeel != null)
         {
-          System.err.println(
-                  "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
-          UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
-        } catch (Throwable e)
+          try
+          {
+            UIManager.setLookAndFeel(lookAndFeel);
+          } catch (Throwable e)
+          {
+            System.err.println(
+                    "Failed to set QuaQua look and feel: " + e.toString());
+          }
+        }
+        if (lookAndFeel == null
+                || !(lookAndFeel.getClass().isAssignableFrom(
+                        UIManager.getLookAndFeel().getClass()))
+                || !UIManager.getLookAndFeel().getClass().toString()
+                        .toLowerCase().contains("quaqua"))
         {
-          System.err.println(
-                  "Failed to reset look and feel: " + e.toString());
+          try
+          {
+            System.err.println(
+                    "Quaqua LaF not available on this plaform. Using VAqua(4).\nSee https://issues.jalview.org/browse/JAL-2976");
+            UIManager.setLookAndFeel("org.violetlib.aqua.AquaLookAndFeel");
+          } catch (Throwable e)
+          {
+            System.err.println(
+                    "Failed to reset look and feel: " + e.toString());
+          }
         }
       }
     }
-
     /*
      * configure 'full' SO model if preferences say to, else use the default (full SO)
      * - as JS currently doesn't have OBO parsing, it must use 'Lite' version
      */
-    boolean soDefault = !Platform.isJS();
+    boolean soDefault = !isJS;
     if (Cache.getDefault("USE_FULL_SO", soDefault))
     {
-      SequenceOntologyFactory.setInstance(new SequenceOntology());
+      SequenceOntologyFactory.setSequenceOntology(new SequenceOntology());
     }
 
+
+    desktop = null;
     if (!headless)
     {
-      desktop = new Desktop();
+      desktop = Desktop.getInstance();
       desktop.setInBatchMode(true); // indicate we are starting up
-
       try
       {
         JalviewTaskbar.setTaskbar(this);
@@ -437,13 +460,14 @@ public class Jalview
       {
         System.out.println("Error setting Taskbar: " + t.getMessage());
       }
-
       desktop.setVisible(true);
       if (Platform.isJS())
         Cache.setProperty("SHOW_JWS2_SERVICES", "false");
-      desktop.startServiceDiscovery();
-
-      if (!Platform.isJS())
+      if (allowServices)
+      {
+        desktop.startServiceDiscovery();
+      }
+      if (!isJS)
       /**
        * Java only
        * 
@@ -501,45 +525,130 @@ public class Jalview
         BioJsHTMLOutput.updateBioJS();
       }
     }
+    parseArguments(aparser, true);
+  }
 
-    // Move any new getdown-launcher-new.jar into place over old
-    // getdown-launcher.jar
-    String appdirString = System.getProperty("getdownappdir");
-    if (appdirString != null && appdirString.length() > 0)
+  /**
+   * Parse all command-line String[] arguments as well as all JavaScript-derived
+   * parameters from Info.
+   * 
+   * We allow for this method to be run from JavaScript. Basically allowing
+   * simple scripting.
+   * 
+   * @param aparser
+   * @param isStartup
+   */
+  public void parseArguments(ArgsParser aparser, boolean isStartup)
+  {
+
+    String groovyscript = null; // script to execute after all loading is
+    boolean isJS = Platform.isJS();
+    if (!isJS)
+    /** @j2sIgnore */
     {
-      final File appdir = new File(appdirString);
-      new Thread()
+      // Move any new getdown-launcher-new.jar into place over old
+      // getdown-launcher.jar
+      String appdirString = System.getProperty("getdownappdir");
+      if (appdirString != null && appdirString.length() > 0)
       {
-        @Override
-        public void run()
+        final File appdir = new File(appdirString);
+        new Thread()
         {
-          LaunchUtil.upgradeGetdown(
-                  new File(appdir, "getdown-launcher-old.jar"),
-                  new File(appdir, "getdown-launcher.jar"),
-                  new File(appdir, "getdown-launcher-new.jar"));
-        }
-      }.start();
-    }
+          @Override
+          public void run()
+          {
+            LaunchUtil.upgradeGetdown(
+                    new File(appdir, "getdown-launcher-old.jar"),
+                    new File(appdir, "getdown-launcher.jar"),
+                    new File(appdir, "getdown-launcher-new.jar"));
+          }
+        }.start();
+      }
 
-    String file = null, data = null;
-    FileFormatI format = null;
-    DataSourceType protocol = null;
-    FileLoader fileLoader = new FileLoader(!headless);
+      // completed one way or another
+      // extract groovy argument and execute if necessary
+      groovyscript = aparser.getValue("groovy", true);
 
-    String groovyscript = null; // script to execute after all loading is
-    // completed one way or another
-    // extract groovy argument and execute if necessary
-    groovyscript = aparser.getValue("groovy", true);
-    file = aparser.getValue("open", true);
+    }
+
+    String file = aparser.getValue("open", true);
 
-    if (file == null && desktop == null)
+    if (!isJS && file == null && desktop == null)
     {
       System.out.println("No files to open!");
       System.exit(1);
     }
+
+    setDisplayParameters(aparser);
+    
+    // time to open a file.
+
     long progress = -1;
+    DataSourceType protocol = null;
+    FileLoader fileLoader = new FileLoader(!headless);
+    FileFormatI format = null;
     // Finally, deal with the remaining input data.
-    if (file != null)
+    AlignFrame af = null;
+
+    JalviewJSApp jsApp = (isJS ? new JalviewJSApp(this, aparser) : null);
+
+    if (file == null)
+    {
+      if (isJS)
+      {
+        // JalviewJS allows sequence1 sequence2 ....
+        
+      }
+      else if (!headless && Cache.getDefault("SHOW_STARTUP_FILE", true))
+      /**
+       * Java only
+       * 
+       * @j2sIgnore
+       */
+      {
+
+        // We'll only open the default file if the desktop is visible.
+        // And the user
+        // ////////////////////
+
+        file = Cache.getDefault("STARTUP_FILE",
+                Cache.getDefault("www.jalview.org",
+                        "http://www.jalview.org")
+                        + "/examples/exampleFile_2_7.jar");
+        if (file.equals(
+                "http://www.jalview.org/examples/exampleFile_2_3.jar"))
+        {
+          // hardwire upgrade of the startup file
+          file.replace("_2_3.jar", "_2_7.jar");
+          // and remove the stale setting
+          Cache.removeProperty("STARTUP_FILE");
+        }
+
+        protocol = DataSourceType.FILE;
+
+        if (file.indexOf("http:") > -1)
+        {
+          protocol = DataSourceType.URL;
+        }
+
+        if (file.endsWith(".jar"))
+        {
+          format = FileFormat.Jalview;
+        }
+        else
+        {
+          try
+          {
+            format = new IdentifyFile().identify(file, protocol);
+          } catch (FileFormatException e)
+          {
+            // TODO what?
+          }
+        }
+        af = fileLoader.LoadFileWaitTillLoaded(file, protocol, format);
+      }
+    }
+    else
     {
       if (!headless)
       {
@@ -551,11 +660,11 @@ public class Jalview
       System.out.println("CMD [-open " + file + "] executed successfully!");
 
       if (!Platform.isJS())
-        /**
-         * ignore in JavaScript -- can't just file existence - could load it?
-         * 
-         * @j2sIgnore
-         */
+      /**
+       * ignore in JavaScript -- can't just file existence - could load it?
+       * 
+       * @j2sIgnore
+       */
       {
         if (!file.startsWith("http://") && !file.startsWith("https://"))
         // BH 2019 added https check for Java
@@ -570,210 +679,91 @@ public class Jalview
           }
         }
       }
-
-        protocol = AppletFormatAdapter.checkProtocol(file);
-
+      
+      String fileFormat = (isJS
+              ? (String) aparser.getAppletValue("format", null, true)
+              : null);
+      protocol = AppletFormatAdapter.checkProtocol(file);
       try
       {
-        format = new IdentifyFile().identify(file, protocol);
+        format = (fileFormat != null
+                ? FileFormats.getInstance().forName(fileFormat)
+                : null);
+        if (format == null)
+        {
+          format = new IdentifyFile().identify(file, protocol);
+        }
       } catch (FileFormatException e1)
       {
         // TODO ?
       }
 
-      AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
+      af = new FileLoader(!headless).LoadFileWaitTillLoaded(file, protocol,
               format);
       if (af == null)
       {
-        System.out.println("error");
+        System.out.println("jalview error - AlignFrame was not created");
       }
       else
       {
-        setCurrentAlignFrame(af);
-        data = aparser.getValue("colour", true);
-        if (data != null)
-        {
-          data.replaceAll("%20", " ");
-
-          ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
-                  af.getViewport(), af.getViewport().getAlignment(), data);
-
-          if (cs != null)
-          {
-            System.out.println(
-                    "CMD [-color " + data + "] executed successfully!");
-          }
-          af.changeColour(cs);
-        }
 
-        // Must maintain ability to use the groups flag
-        data = aparser.getValue("groups", true);
-        if (data != null)
-        {
-          af.parseFeaturesFile(data,
-                  AppletFormatAdapter.checkProtocol(data));
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD groups[-" + data + "]  executed successfully!");
-        }
-        data = aparser.getValue("features", true);
-        if (data != null)
-        {
-          af.parseFeaturesFile(data,
-                  AppletFormatAdapter.checkProtocol(data));
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD [-features " + data + "]  executed successfully!");
-        }
-
-        data = aparser.getValue("annotations", true);
-        if (data != null)
-        {
-          af.loadJalviewDataFile(data, null, null, null);
-          // System.out.println("Added " + data);
-          System.out.println(
-                  "CMD [-annotations " + data + "] executed successfully!");
-        }
-        // set or clear the sortbytree flag.
-        if (aparser.contains("sortbytree"))
+        // JalviewLite interface for JavaScript allows second file open
+        String file2 = aparser.getValue(ArgsParser.OPEN2, true);
+        if (file2 != null)
         {
-          af.getViewport().setSortByTree(true);
-          if (af.getViewport().getSortByTree())
+          protocol = AppletFormatAdapter.checkProtocol(file2);
+          try
           {
-            System.out.println("CMD [-sortbytree] executed successfully!");
-          }
-        }
-        if (aparser.contains("no-annotation"))
-        {
-          af.getViewport().setShowAnnotation(false);
-          if (!af.getViewport().isShowAnnotation())
+            format = new IdentifyFile().identify(file2, protocol);
+          } catch (FileFormatException e1)
           {
-            System.out.println("CMD no-annotation executed successfully!");
+            // TODO ?
           }
-        }
-        if (aparser.contains("nosortbytree"))
-        {
-          af.getViewport().setSortByTree(false);
-          if (!af.getViewport().getSortByTree())
+          AlignFrame af2 = new FileLoader(!headless)
+                  .LoadFileWaitTillLoaded(file2, protocol, format);
+          if (af2 == null)
           {
-            System.out
-                    .println("CMD [-nosortbytree] executed successfully!");
+            System.out.println("error");
           }
-        }
-        data = aparser.getValue("tree", true);
-        if (data != null)
-        {
-          try
+          else
           {
+            AlignViewport.openLinkedAlignmentAs(af,
+                    af.getViewport().getAlignment(),
+                    af2.getViewport().getAlignment(), "",
+                    AlignViewport.SPLIT_FRAME);
             System.out.println(
-                    "CMD [-tree " + data + "] executed successfully!");
-            NewickFile nf = new NewickFile(data,
-                    AppletFormatAdapter.checkProtocol(data));
-            af.getViewport()
-                    .setCurrentTree(af.showNewickTree(nf, data).getTree());
-          } catch (IOException ex)
-          {
-            System.err.println("Couldn't add tree " + data);
-            ex.printStackTrace(System.err);
+                    "CMD [-open2 " + file2 + "] executed successfully!");
           }
         }
-        // TODO - load PDB structure(s) to alignment JAL-629
-        // (associate with identical sequence in alignment, or a specified
-        // sequence)
-        if (groovyscript != null)
+        setCurrentAlignFrame(af);
+
+        setFrameDependentProperties(aparser, af);
+        
+        if (isJS)
         {
-          // Execute the groovy script after we've done all the rendering stuff
-          // and before any images or figures are generated.
-          System.out.println("Executing script " + groovyscript);
-          executeGroovyScript(groovyscript, af);
-          System.out.println("CMD groovy[" + groovyscript
-                  + "] executed successfully!");
-          groovyscript = null;
+          jsApp.initFromParams(af);
         }
-        String imageName = "unnamed.png";
-        while (aparser.getSize() > 1)
+        else
+        /**
+         * Java only
+         * 
+         * @j2sIgnore
+         */
         {
-          String outputFormat = aparser.nextValue();
-          file = aparser.nextValue();
-
-          if (outputFormat.equalsIgnoreCase("png"))
-          {
-            af.createPNG(new File(file));
-            imageName = (new File(file)).getName();
-            System.out.println("Creating PNG image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("svg"))
-          {
-            File imageFile = new File(file);
-            imageName = imageFile.getName();
-            af.createSVG(imageFile);
-            System.out.println("Creating SVG image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("html"))
-          {
-            File imageFile = new File(file);
-            imageName = imageFile.getName();
-            HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
-            htmlSVG.exportHTML(file);
-
-            System.out.println("Creating HTML image: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("biojsmsa"))
+          if (groovyscript != null)
           {
-            if (file == null)
-            {
-              System.err.println("The output html file must not be null");
-              return;
-            }
-            try
-            {
-              BioJsHTMLOutput.refreshVersionInfo(
-                      BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
-            } catch (URISyntaxException e)
-            {
-              e.printStackTrace();
-            }
-            BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
-            bjs.exportHTML(file);
-            System.out
-                    .println("Creating BioJS MSA Viwer HTML file: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("imgMap"))
-          {
-            af.createImageMap(new File(file), imageName);
-            System.out.println("Creating image map: " + file);
-            continue;
-          }
-          else if (outputFormat.equalsIgnoreCase("eps"))
-          {
-            File outputFile = new File(file);
-            System.out.println(
-                    "Creating EPS file: " + outputFile.getAbsolutePath());
-            af.createEPS(outputFile);
-            continue;
-          }
-
-          af.saveAlignment(file, format);
-          if (af.isSaveAlignmentSuccessful())
-          {
-            System.out.println("Written alignment in " + format
-                    + " format to " + file);
-          }
-          else
-          {
-            System.out.println("Error writing file " + file + " in "
-                    + format + " format!!");
+            // Execute the groovy script after we've done all the rendering
+            // stuff
+            // and before any images or figures are generated.
+            System.out.println("Executing script " + groovyscript);
+            executeGroovyScript(groovyscript, af);
+            System.out.println("CMD groovy[" + groovyscript
+                    + "] executed successfully!");
+            groovyscript = null;
           }
-
         }
-
-        while (aparser.getSize() > 0)
-        {
-          System.out.println("Unknown arg: " + aparser.nextValue());
+        if (!isJS || !isStartup) {
+          createOutputFiles(aparser, format);
         }
       }
       if (headless)
@@ -781,82 +771,315 @@ public class Jalview
         af.getViewport().getCalcManager().shutdown();
       }
     }
-    AlignFrame startUpAlframe = null;
-    // We'll only open the default file if the desktop is visible.
-    // And the user
-    // ////////////////////
+    // extract groovy arguments before anything else.
+    // Once all other stuff is done, execute any groovy scripts (in order)
+    if (!isJS && groovyscript != null)
+    {
+      if (Cache.groovyJarsPresent())
+      {
+        System.out.println("Executing script " + groovyscript);
+        executeGroovyScript(groovyscript, af);
+      }
+      else
+      {
+        System.err.println(
+                "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
+                        + groovyscript);
+      }
+    }
 
-    if (!Platform.isJS() && !headless && file == null
-            && Cache.getDefault("SHOW_STARTUP_FILE", true))
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
+    // and finally, turn off batch mode indicator - if the desktop still exists
+    if (desktop != null)
     {
-      file = Cache.getDefault("STARTUP_FILE",
-              Cache.getDefault("www.jalview.org",
-                      "http://www.jalview.org")
-                      + "/examples/exampleFile_2_7.jar");
-      if (file.equals(
-              "http://www.jalview.org/examples/exampleFile_2_3.jar"))
+      if (progress != -1)
       {
-        // hardwire upgrade of the startup file
-        file.replace("_2_3.jar", "_2_7.jar");
-        // and remove the stale setting
-        Cache.removeProperty("STARTUP_FILE");
+        desktop.setProgressBar(null, progress);
       }
+      desktop.setInBatchMode(false);
+    }
+    
+    if (jsApp != null) {
+      jsApp.callInitCallback();
+    }
+  }
+  
+  /**
+   * Set general display parameters irrespective of file loading or headlessness.
+   * 
+   * @param aparser
+   */
+  private void setDisplayParameters(ArgsParser aparser)
+  {
+    if (aparser.contains(ArgsParser.NOMENUBAR))
+    {
+      noMenuBar = true;
+      System.out.println("CMD [nomenu] executed successfully!");
+    }
+
+    if (aparser.contains(ArgsParser.NOSTATUS))
+    {
+      noStatus = true;
+      System.out.println("CMD [nostatus] executed successfully!");
+    }
+
+    if (aparser.contains(ArgsParser.NOANNOTATION)
+            || aparser.contains(ArgsParser.NOANNOTATION2))
+    {
+      noAnnotation = true;
+      System.out.println("CMD no-annotation executed successfully!");
+    }
+    if (aparser.contains(ArgsParser.NOCALCULATION))
+    {
+      noCalculation = true;
+      System.out.println("CMD [nocalculation] executed successfully!");
+    }
+  }
+
+
+  private void setFrameDependentProperties(ArgsParser aparser,
+          AlignFrame af)
+  {
+    String data = aparser.getValue(ArgsParser.COLOUR, true);
+    if (data != null)
+    {
+      data.replaceAll("%20", " ");
 
-      protocol = DataSourceType.FILE;
+      ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
+              af.getViewport(), af.getViewport().getAlignment(), data);
 
-      if (file.indexOf("http:") > -1)
+      if (cs != null)
       {
-        protocol = DataSourceType.URL;
+        System.out.println(
+                "CMD [-color " + data + "] executed successfully!");
       }
+      af.changeColour(cs);
+    }
+
+    // Must maintain ability to use the groups flag
+    data = aparser.getValue(ArgsParser.GROUPS, true);
+    if (data != null)
+    {
+      af.parseFeaturesFile(data,
+              AppletFormatAdapter.checkProtocol(data));
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD groups[-" + data + "]  executed successfully!");
+    }
+    data = aparser.getValue(ArgsParser.FEATURES, true);
+    if (data != null)
+    {
+      af.parseFeaturesFile(data,
+              AppletFormatAdapter.checkProtocol(data));
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD [-features " + data + "]  executed successfully!");
+    }
+    data = aparser.getValue(ArgsParser.ANNOTATIONS, true);
+    if (data != null)
+    {
+      af.loadJalviewDataFile(data, null, null, null);
+      // System.out.println("Added " + data);
+      System.out.println(
+              "CMD [-annotations " + data + "] executed successfully!");
+    }
+
+    // JavaScript feature
 
-      if (file.endsWith(".jar"))
+    if (aparser.contains(ArgsParser.SHOWOVERVIEW))
+    {
+      af.overviewMenuItem_actionPerformed(null);
+      System.out.println("CMD [showoverview] executed successfully!");
+    }
+
+    // set or clear the sortbytree flag.
+    if (aparser.contains(ArgsParser.SORTBYTREE))
+    {
+      af.getViewport().setSortByTree(true);
+      if (af.getViewport().getSortByTree())
       {
-        format = FileFormat.Jalview;
+        System.out.println("CMD [-sortbytree] executed successfully!");
       }
-      else
+    }
+
+    boolean doUpdateAnnotation = false;
+    /**
+     * we do this earlier in JalviewJS because of a complication with
+     * SHOWOVERVIEW
+     * 
+     * For now, just fixing this in JalviewJS.
+     *
+     * 
+     * @j2sIgnore
+     * 
+     */
+    {
+      if (noAnnotation)
       {
-        try
-        {
-          format = new IdentifyFile().identify(file, protocol);
-        } catch (FileFormatException e)
+        af.getViewport().setShowAnnotation(false);
+        if (!af.getViewport().isShowAnnotation())
         {
-          // TODO what?
+          doUpdateAnnotation = true;
         }
       }
-
-      startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
-              format);
-      // extract groovy arguments before anything else.
     }
 
-    // Once all other stuff is done, execute any groovy scripts (in order)
-    if (groovyscript != null)
+    if (aparser.contains(ArgsParser.NOSORTBYTREE))
     {
-      if (Cache.groovyJarsPresent())
+      af.getViewport().setSortByTree(false);
+      if (!af.getViewport().getSortByTree())
       {
-        System.out.println("Executing script " + groovyscript);
-        executeGroovyScript(groovyscript, startUpAlframe);
+        doUpdateAnnotation = true;
+        System.out
+                .println("CMD [-nosortbytree] executed successfully!");
       }
-      else
+    }
+    if (doUpdateAnnotation)
+    { // BH 2019.07.24
+      af.setMenusForViewport();
+      af.alignPanel.updateLayout();
+    }
+
+    data = aparser.getValue(ArgsParser.TREE, true);
+    if (data != null)
+    {
+      try
       {
-        System.err.println(
-                "Sorry. Groovy Support is not available, so ignoring the provided groovy script "
-                        + groovyscript);
+        NewickFile nf = new NewickFile(data,
+                AppletFormatAdapter.checkProtocol(data));
+        af.getViewport()
+                .setCurrentTree(af.showNewickTree(nf, data).getTree());
+        System.out.println(
+                "CMD [-tree " + data + "] executed successfully!");
+      } catch (IOException ex)
+      {
+        System.err.println("Couldn't add tree " + data);
+        ex.printStackTrace(System.err);
       }
     }
-    // and finally, turn off batch mode indicator - if the desktop still exists
-    if (desktop != null)
+    // TODO - load PDB structure(s) to alignment JAL-629
+    // (associate with identical sequence in alignment, or a specified
+    // sequence)
+
+  }
+
+  /**
+   * Writes an output file for each format (if any) specified in the
+   * command-line arguments. Supported formats are currently
+   * <ul>
+   * <li>png</li>
+   * <li>svg</li>
+   * <li>html</li>
+   * <li>biojsmsa</li>
+   * <li>imgMap</li>
+   * <li>eps</li>
+   * </ul>
+   * A format parameter should be followed by a parameter specifying the output
+   * file name. {@code imgMap} parameters should follow those for the
+   * corresponding alignment image output.
+   * 
+   * @param aparser
+   * @param format
+   */
+  private void createOutputFiles(ArgsParser aparser, FileFormatI format)
+  {
+    AlignFrame af = currentAlignFrame;
+    while (aparser.getSize() >= 2)
     {
-      if (progress != -1)
+      String outputFormat = aparser.nextValue();
+      File imageFile;
+      String fname;
+      switch (outputFormat.toLowerCase())
       {
-        desktop.setProgressBar(null, progress);
+      case "png":
+        imageFile = new File(aparser.nextValue());
+        af.createPNG(imageFile);
+        System.out.println(
+                "Creating PNG image: " + imageFile.getAbsolutePath());
+        continue;
+      case "svg":
+        imageFile = new File(aparser.nextValue());
+        af.createSVG(imageFile);
+        System.out.println(
+                "Creating SVG image: " + imageFile.getAbsolutePath());
+        continue;
+      case "eps":
+        imageFile = new File(aparser.nextValue());
+        System.out.println(
+                "Creating EPS file: " + imageFile.getAbsolutePath());
+        af.createEPS(imageFile);
+        continue;
+      case "biojsmsa":
+        fname = new File(aparser.nextValue()).getAbsolutePath();
+        try
+        {
+          BioJsHTMLOutput.refreshVersionInfo(
+                  BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
+        } catch (URISyntaxException e)
+        {
+          e.printStackTrace();
+        }
+        BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
+        bjs.exportHTML(fname);
+        System.out.println("Creating BioJS MSA Viwer HTML file: " + fname);
+        continue;
+      case "html":
+        fname = new File(aparser.nextValue()).getAbsolutePath();
+        HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
+        htmlSVG.exportHTML(fname);
+        System.out.println("Creating HTML image: " + fname);
+        continue;
+      case "imgmap":
+        imageFile = new File(aparser.nextValue());
+        af.alignPanel.makePNGImageMap(imageFile, "unnamed.png");
+        System.out.println(
+                "Creating image map: " + imageFile.getAbsolutePath());
+        continue;
+      default:
+        // fall through - try to parse as an alignment data export format
+        FileFormatI outFormat = null;
+        try
+        {
+          outFormat = FileFormats.getInstance().forName(outputFormat);
+        } catch (Exception formatP)
+        {
+        }
+        if (outFormat == null)
+        {
+          System.out.println("Couldn't parse " + outputFormat
+                  + " as a valid Jalview format string.");
+          continue;
+        }
+        if (!outFormat.isWritable())
+        {
+          System.out.println(
+                  "This version of Jalview does not support alignment export as "
+                          + outputFormat);
+          continue;
+        }
+        // record file as it was passed to Jalview so it is recognisable to the CLI
+        // caller
+        String file;
+        fname = new File(file = aparser.nextValue()).getAbsolutePath();
+        // JBPNote - yuck - really wish we did have a bean returned from this which gave
+        // success/fail like before !
+        af.saveAlignment(fname, outFormat);
+        if (!af.isSaveAlignmentSuccessful())
+        {
+          System.out.println("Written alignment in " + outputFormat
+                  + " format to " + file);
+          continue;
+        }
+        else
+        {
+          System.out.println("Error writing file " + file + " in "
+                  + outputFormat + " format!!");
+        }
       }
-      desktop.setInBatchMode(false);
+    }
+    // ??? Should report - 'ignoring' extra args here...
+    while (aparser.getSize() > 0)
+    {
+      System.out.println("Ignoring extra argument: " + aparser.nextValue());
     }
   }
 
@@ -906,7 +1129,7 @@ public class Jalview
     /**
      * start a User Config prompt asking if we can log usage statistics.
      */
-    PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop,
+    PromptUserConfig prompter = new PromptUserConfig(Desktop.getDesktopPane(),
             "USAGESTATS", "Jalview Usage Statistics",
             "Do you want to help make Jalview better by enabling "
                     + "the collection of usage statistics with Google Analytics ?"
@@ -936,10 +1159,10 @@ public class Jalview
    * Locate the given string as a file and pass it to the groovy interpreter.
    * 
    * @param groovyscript
-   *                         the script to execute
+   *          the script to execute
    * @param jalviewContext
-   *                         the Jalview Desktop object passed in to the groovy
-   *                         binding as the 'Jalview' object.
+   *          the Jalview Desktop object passed in to the groovy binding as the
+   *          'Jalview' object.
    */
   private void executeGroovyScript(String groovyscript, AlignFrame af)
   {
@@ -1068,8 +1291,8 @@ public class Jalview
   }
 
   /**
-   * Quit method delegates to Desktop.quit - unless running in headless mode when
-   * it just ends the JVM
+   * Quit method delegates to Desktop.quit - unless running in headless mode
+   * when it just ends the JVM
    */
   public void quit()
   {
@@ -1085,11 +1308,32 @@ public class Jalview
 
   public static AlignFrame getCurrentAlignFrame()
   {
-    return Jalview.currentAlignFrame;
+    return Jalview.getInstance().currentAlignFrame;
   }
 
   public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
   {
-    Jalview.currentAlignFrame = currentAlignFrame;
+    Jalview.getInstance().currentAlignFrame = currentAlignFrame;
   }
+
+  
+  public void notifyWorker(AlignCalcWorkerI worker, String status)
+  {
+    // System.out.println("Jalview worker " + worker.getClass().getSimpleName()
+    // + " " + status);
+  }
+
+
+  private static boolean isInteractive = true;
+
+  public static boolean isInteractive()
+  {
+    return isInteractive;
+  }
+
+  public static void setInteractive(boolean tf)
+  {
+    isInteractive = tf;
+  }
+
 }
index 26110f8..61b3487 100644 (file)
@@ -20,16 +20,37 @@ public class JalviewJS2
 
   static {
     /**
-     * @j2sNative
+     * @ could do it this way:
      * 
-     *            J2S.thisApplet.__Info.args =
-     *            ["open","examples/uniref50.fa","features",
-     *            "examples/exampleFeatures.txt"];
+     * j2sNative
+     * 
+     * J2S.thisApplet.__Info.args = [ "open","examples/uniref50.fa",
+     * "features","examples/exampleFeatures.txt", "noannotation" ];
      */
   }
 
   public static void main(String[] args) throws Exception
   {
+    if (args.length == 0)
+    {
+      args = new String[] {
+        //  "headless",
+          "open", "examples/uniref50.fa",
+//          "features",
+//          "examples/exampleFeatures.txt"
+//          , "noannotation"
+          //, "showoverview"
+          //, "png", "test-bh.png"
+      };
+    }
+
+    // String cmds = "nodisplay -open examples/uniref50.fa -sortbytree -props
+    // test/jalview/io/testProps.jvprops -colour zappo "
+    // + "-jabaws http://www.compbio.dundee.ac.uk/jabaws -nosortbytree "
+    // + "-features examples/testdata/plantfdx.features -annotations
+    // examples/testdata/plantfdx.annotations -tree
+    // examples/testdata/uniref50_test_tree";
+    // args = cmds.split(" ");
     Jalview.main(args);
        //showFocusTimer();
 }
diff --git a/src/jalview/bin/JalviewJSApp.java b/src/jalview/bin/JalviewJSApp.java
new file mode 100644 (file)
index 0000000..feab4ae
--- /dev/null
@@ -0,0 +1,1691 @@
+package jalview.bin;
+
+import java.awt.EventQueue;
+//import java.applet.AppletContext;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.SwingUtilities;
+
+import jalview.api.JalviewJSApi;
+import jalview.api.StructureSelectionManagerProvider;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.CalculationChooser;
+import jalview.gui.Desktop;
+import jalview.gui.StructureViewer;
+import jalview.io.AnnotationFile;
+import jalview.io.AppletFormatAdapter;
+import jalview.io.DataSourceType;
+import jalview.io.FeaturesFile;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.IdentifyFile;
+import jalview.io.JPredFile;
+import jalview.io.JnetAnnotationMaker;
+import jalview.io.NewickFile;
+import jalview.structure.SelectionListener;
+import jalview.structure.SelectionSource;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.HttpUtils;
+import jalview.util.Platform;
+
+/**
+ * Basically the JalviewLite application, but without JalviewLite
+ * 
+ * Processing all "applet parameters" and also all "applet interface" methods.
+ * 
+ * @author hansonr
+ *
+ */
+public class JalviewJSApp implements JalviewJSApi
+{
+  private ArgsParser aparser;
+
+  private String[] ret = new String[1];
+
+  // private boolean alignPDBStructures; From JalviewLite; not implemented
+
+  private String separator = "\u00AC"; // JalviewLite note: the default used to
+                                       // be '|', but many sequence IDS include
+                                       // pipes.
+
+  /**
+   * We maintain a pointer to the jalview instance here, because only with that
+   * do we have a direct connection from the JavaScript "applet" object to the
+   * proper instance of Jalview in case there are multiple applets on a page.
+   */
+  private Jalview jalview;
+
+  public class JsSelectionListener
+          implements jalview.structure.SelectionListener
+  {
+
+    AlignFrame _alf;
+
+    String _listener;
+
+    public JsSelectionListener(AlignFrame alf, String listener)
+    {
+      _alf = alf;
+      _listener = listener;
+    }
+
+    public boolean isFor(AlignFrame alf, String listener)
+    {
+      return (_alf == null || _alf == alf) && _listener.equals(listener);
+    }
+
+    @Override
+    public void selection(SequenceGroup seqsel, ColumnSelection colsel,
+            HiddenColumns hidden, SelectionSource source)
+    {
+      // System.err.println("Testing selection event relay to
+      // jsfunction:"+_listener);
+      String setid = "";
+      AlignFrame srcFrame = (_alf == null ? getCurrentAlignFrame() : _alf);
+      if (source != null)
+      {
+        if (source instanceof AlignViewport
+                && srcFrame.getViewport() != source)
+        {
+          return;
+        }
+      }
+      String[] seqs = new String[] {};
+      String[] cols = new String[] {};
+      int strt = 0, end = (srcFrame == null) ? -1
+              : srcFrame.alignPanel.av.getAlignment().getWidth();
+      if (seqsel != null && seqsel.getSize() > 0)
+      {
+        seqs = new String[seqsel.getSize()];
+        for (int i = 0; i < seqs.length; i++)
+        {
+          seqs[i] = seqsel.getSequenceAt(i).getName();
+        }
+        if (strt < seqsel.getStartRes())
+        {
+          strt = seqsel.getStartRes();
+        }
+        if (end == -1 || end > seqsel.getEndRes())
+        {
+          end = seqsel.getEndRes();
+        }
+      }
+      if (colsel != null && !colsel.isEmpty())
+      {
+        if (end == -1)
+        {
+          end = colsel.getMax() + 1;
+        }
+        cols = new String[colsel.getSelected().size()];
+        for (int i = 0; i < cols.length; i++)
+        {
+          cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
+        }
+      }
+      else
+      {
+        if (seqsel != null && seqsel.getSize() > 0)
+        {
+          // send a valid range, otherwise we send the empty selection
+          cols = new String[1];
+          cols[0] = "" + (1 + strt) + "-" + (1 + end);
+        }
+      }
+      doSendCallback(_listener,
+              new Object[]
+              { Jalview.getInstance().j2sAppletID, srcFrame, source, setid,
+                  seqs, cols });
+    }
+
+  }
+
+  public JalviewJSApp(Jalview jalview, ArgsParser aparser)
+  {
+    Platform.setAppClass(this);
+    this.jalview = jalview;
+    this.aparser = aparser;
+  }
+
+  @Override
+  public boolean addPdbFile(String sequenceId, String pdbId, String pdbFile,
+          AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    SequenceI seq = alf.getViewport().getAlignment().findName(sequenceId);
+    if (seq != null)
+    {
+      Vector<PDBEntry> pdbe = seq.getAllPDBEntries();
+      PDBEntry pdbentry = null;
+      if (pdbe != null && pdbe.size() > 0)
+      {
+        for (int pe = 0, peSize = pdbe.size(); pe < peSize; pe++)
+        {
+          pdbentry = pdbe.elementAt(pe);
+          if (!pdbentry.getId().equals(pdbId)
+                  || pdbFile != null && !pdbentry.getFile().equals(pdbFile))
+          {
+            pdbentry = null;
+          }
+        }
+      }
+      if (pdbentry == null)
+      {
+        pdbentry = new PDBEntry(pdbId, null, pdbFile);
+        if (pdbFile != null)
+        {
+          DataSourceType protocol = AppletFormatAdapter
+                  .resolveProtocol(pdbFile, FileFormat.PDB);
+          if (protocol == null)
+            return false;
+          pdbentry.setProperty("protocol", protocol);
+        }
+        seq.addPDBId(pdbentry);
+        alf.alignPanel.getStructureSelectionManager()
+                .registerPDBEntry(pdbentry);
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public String getAlignment(String format, boolean addSuffix,
+          AlignFrame alf)
+  {
+    try
+    {
+      if (alf == null)
+      {
+        alf = getCurrentAlignFrame();
+      }
+
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
+      String reply = new AppletFormatAdapter().formatSequences(theFormat,
+              alf.getViewport().getAlignment(), addSuffix);
+      return reply;
+    } catch (IllegalArgumentException ex)
+    {
+      ex.printStackTrace();
+      return "Error retrieving alignment, possibly invalid format specifier: "
+              + format;
+    }
+  }
+
+  @Override
+  public String[] getAlignmentOrder(AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    AlignmentI alorder = alf.getViewport().getAlignment();
+    String[] order = new String[alorder.getHeight()];
+    for (int i = 0; i < order.length; i++)
+    {
+      order[i] = alorder.getSequenceAt(i).getName();
+    }
+    return order;// arrayToSeparatorList(order, sep);
+  }
+
+  @Override
+  public String getAnnotation(AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    String annotation = new AnnotationFile()
+            .printAnnotationsForView(alf.getViewport());
+    return annotation;
+  }
+
+  /**
+   * Get the applet-like code base even though this is an application.
+   */
+
+  @Override
+  public URL getCodeBase()
+  {
+    return Platform.getCodeBase();
+  }
+
+  @Override
+  public AlignFrame getCurrentAlignFrame()
+  {
+    // if (jalview != Jalview.getInstance() || jalview.currentAlignFrame !=
+    // Jalview.getCurrentAlignFrame()) {
+    // /** @j2sNative debugger */
+    // }
+    return jalview.currentAlignFrame;
+  }
+
+  /**
+   * Get the applet-like document base even though this is an application.
+   */
+
+  @Override
+  public URL getDocumentBase()
+  {
+    return Platform.getDocumentBase();
+  }
+
+  @Override
+  public String[] getFeatureGroups(AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return alf.getFeatureGroups();
+  }
+
+  @Override
+  public String[] getFeatureGroupsOfState(boolean visible, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return alf.getFeatureGroupsOfState(visible);
+  }
+
+  /**
+   * JavaScript interface to print the alignment frame
+   * 
+   * @param format
+   *          "jalview" or "gff" with or without ";includeComplement" or
+   *          ";includeNonpositional"; default with no ";" is
+   *          ";includeNonpositional"
+   * @param alf
+   * 
+   * @return
+   */
+  @Override
+  public String getFeatures(String format, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    String features;
+    FeaturesFile formatter = new FeaturesFile();
+    format = format.toLowerCase();
+    if (format.indexOf(";") < 0)
+      format += ";includenonpositional";
+    boolean nonpos = format.indexOf(";includenonpositional") >= 0;
+    boolean compl = format.indexOf(";includecomplement") >= 0;
+    if (format.startsWith("jalview"))
+    {
+      features = formatter.printJalviewFormat(
+              alf.getViewport().getAlignment().getSequencesArray(),
+              alf.alignPanel.getFeatureRenderer(), nonpos, compl);
+    }
+    else
+    {
+      features = formatter.printGffFormat(
+              alf.getViewport().getAlignment().getSequencesArray(),
+              alf.alignPanel.getFeatureRenderer(), nonpos, compl);
+    }
+
+    if (features == null)
+    {
+      features = "";
+    }
+    return features;
+
+  }
+
+  /**
+   * Get an applet parameter as a string.
+   * 
+   */
+  @Override
+  public String getParameter(String name)
+  {
+    return (String) aparser.getAppletValue(name, null, true);
+  }
+
+  /**
+   * Get an applet parameter as an Object.
+   */
+
+  @Override
+  public Object getParameterAsObject(String name)
+  {
+    return aparser.getAppletValue(name, null, false);
+  }
+
+  /**
+   * read sequence1...sequenceN as a raw alignment
+   * 
+   * @param jalviewApp
+   * @return
+   */
+  public String getPastedSequence(JalviewJSApp jalviewApp)
+  {
+    StringBuffer data = new StringBuffer("PASTE");
+    int i = 1;
+    String file = null;
+    while ((file = getParameter("sequence" + i)) != null)
+    {
+      data.append(file.toString() + "\n");
+      i++;
+    }
+    if (data.length() > 5)
+    {
+      file = data.toString();
+    }
+    return file;
+  }
+
+  /**
+   * @j2sAlias getSelectedSequences
+   * 
+   * @see jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
+   *      .AlignFrame)
+   */
+  @Override
+  public SequenceI[] getSelectedSequences(AlignFrame alf)
+  {
+    // return getSelectedSequencesFrom(alf, null);
+    // }
+    //
+    // @Override
+    // public SequenceI[] getSelectedSequencesFrom(AlignFrame alf, String sep)
+    // {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    AlignViewport v = alf.getViewport();
+    if (v.getSelectionGroup() != null)
+    {
+      return v.getSelectionGroup().getSequencesInOrder(v.getAlignment());
+    }
+    return null;
+  }
+  // /**
+  // *
+  // * @see
+  // jalview.appletgui.js.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
+  // * .AlignFrame, java.lang.String)
+  // */
+  // @Override
+  // public void highlight(String sequenceId, String position,
+  // String alignedPosition)
+  // {
+  // highlightIn(null, sequenceId, position, alignedPosition);
+  // }
+
+  /**
+   * @j2sAlias getSelectedSequencesAsAlignment
+   */
+  @Override
+  public String getSelectedSequencesAsAlignment(String format,
+          boolean addSuffix, AlignFrame alf)
+  {
+
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    try
+    {
+      AlignViewport vp = alf.getViewport();
+      FileFormatI theFormat = FileFormats.getInstance().forName(format);
+      if (vp.getSelectionGroup() != null)
+      {
+        // JBPNote: getSelectionAsNewSequence behaviour has changed - this
+        // method now returns a full copy of sequence data
+        // TODO consider using getSequenceSelection instead here
+        String reply = new AppletFormatAdapter().formatSequences(theFormat,
+                new Alignment(vp.getSelectionAsNewSequence()), addSuffix);
+        return reply;
+      }
+    } catch (IllegalArgumentException ex)
+    {
+      ex.printStackTrace();
+      return "Error retrieving alignment, possibly invalid format specifier: "
+              + format;
+    }
+    return "";
+  }
+
+  @Override
+  public void highlight(String sequenceId, String position,
+          String alignedPosition, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    // TODO: could try to highlight in all alignments if alf==null
+    jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+            alf.getViewport().getAlignment().getSequencesArray());
+    final SequenceI sq = matcher.findIdMatch(sequenceId);
+    if (sq != null)
+    {
+      int apos = -1;
+      try
+      {
+        apos = Integer.valueOf(position).intValue();
+        apos--;
+      } catch (NumberFormatException ex)
+      {
+        return;
+      }
+      final int pos = apos;
+      // use vamsas listener to broadcast to all listeners in scope
+      if (alignedPosition != null && (alignedPosition.trim().length() == 0
+              || alignedPosition.toLowerCase().indexOf("false") > -1))
+      {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.getInstance())
+                    .mouseOverVamsasSequence(sq, sq.findIndex(pos), null);
+          }
+        });
+      }
+      else
+      {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+            StructureSelectionManager
+                    .getStructureSelectionManager(Desktop.getInstance())
+                    .mouseOverVamsasSequence(sq, pos, null);
+          }
+        });
+      }
+    }
+  }
+
+  @Override
+  public AlignFrame loadAlignment(String text, String title, int width,
+          int height)
+  {
+    AlignmentI al = null;
+
+    try
+    {
+      FileFormatI format = new IdentifyFile().identify(text,
+              DataSourceType.PASTE);
+      al = new AppletFormatAdapter().readFile(text, DataSourceType.PASTE,
+              format);
+      if (al.getHeight() > 0)
+      {
+        return new AlignFrame(al,
+                width > 0 ? width : AlignFrame.DEFAULT_WIDTH,
+                height > 0 ? height : AlignFrame.DEFAULT_HEIGHT, title);
+      }
+    } catch (IOException ex)
+    {
+      ex.printStackTrace();
+    }
+    return null;
+  }
+
+  @Override
+  public void loadAnnotation(String annotation, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    if (new AnnotationFile().annotateAlignmentView(alf.getViewport(),
+            annotation, DataSourceType.PASTE))
+    {
+      alf.alignPanel.fontChanged();
+      alf.alignPanel.setScrollValues(0, 0);
+    }
+    else
+    {
+      alf.parseFeaturesFile(annotation, DataSourceType.PASTE);
+    }
+  }
+
+  @Override
+  public boolean loadFeatures(String features, boolean autoenabledisplay,
+          AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    boolean ret = alf.parseFeaturesFile(features, DataSourceType.PASTE);
+    if (!ret)
+    {
+      return false;
+    }
+    if (autoenabledisplay)
+    {
+      alf.getViewport().setShowSequenceFeatures(true);
+      // this next was for a checkbox in JalviewLite
+      // ((AlignFrame) alf).getViewport().sequenceFeatures.setState(true);
+    }
+    return true;
+  }
+
+  @Override
+  public boolean loadScoreFile(String fileName, AlignFrame alf)
+  {
+    try
+    {
+      (alf == null ? getCurrentAlignFrame() : alf)
+              .loadJalviewDataFile(fileName, null, null, null);
+      return true;
+    } catch (Throwable t)
+    {
+      return false;
+    }
+  }
+
+  /**
+   * @j2sAlias openPcaPanel
+   * 
+   *           public static method for JalviewJS API to open a PCAPanel without
+   *           necessarily using a dialog.
+   * @param modelName
+   * @param alf
+   * 
+   * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
+   *         if number of sequences selected is inappropriate
+   */
+  @Override
+  public Object openPcaPanel(String modelName, AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return CalculationChooser.openPcaPanel(alf, modelName, null);
+  }
+
+  /**
+   * @j2sAlias openTreePanel
+   * 
+   *           Open a new Tree panel on the desktop statically. Params are
+   *           standard (not set by Groovy). No dialog is opened.
+   * @param treeType
+   * @param modelName
+   * @param alf
+   * 
+   * @return null, or the string "label.you_need_at_least_n_sequences" if number
+   *         of sequences selected is inappropriate
+   */
+  @Override
+  public Object openTreePanel(String treeType, String modelName,
+          AlignFrame alf)
+  {
+    if (alf == null)
+    {
+      alf = getCurrentAlignFrame();
+    }
+    return CalculationChooser.openTreePanel(alf, treeType, modelName, null);
+  }
+
+  @Override
+  public boolean orderAlignment(String[] ids, String undoName,
+          AlignFrame alf)
+  {
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    SequenceI[] sqs = null;
+    if (ids != null && ids.length > 0)
+    {
+      jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+              alf.getViewport().getAlignment().getSequencesArray());
+      int s = 0;
+      sqs = new SequenceI[ids.length];
+      for (int i = 0; i < ids.length; i++)
+      {
+        if (ids[i].trim().length() == 0)
+        {
+          continue;
+        }
+        SequenceI sq = matcher.findIdMatch(ids[i]);
+        if (sq != null)
+        {
+          sqs[s++] = sq;
+        }
+      }
+      if (s > 0)
+      {
+        SequenceI[] sqq = new SequenceI[s];
+        System.arraycopy(sqs, 0, sqq, 0, s);
+        sqs = sqq;
+      }
+      else
+      {
+        sqs = null;
+      }
+    }
+    if (sqs == null)
+    {
+      return false;
+    }
+    ;
+    final AlignmentOrder aorder = new AlignmentOrder(sqs);
+
+    if (undoName != null && undoName.trim().length() == 0)
+    {
+      undoName = null;
+    }
+    final String _undoName = undoName;
+    // TODO: deal with synchronization here: cannot raise any events until
+    // alfter
+    // this has returned.
+    return alf.sortBy(aorder, _undoName);
+  }
+
+  /**
+   * Allow an outside entity to initiate the second half of argument parsing
+   * (only).
+   * 
+   * @param args
+   * @return null is good
+   */
+  @Override
+  public Object parseArguments(String[] args)
+  {
+
+    try
+    {
+      jalview.parseArguments(new ArgsParser(args), false);
+      return null;
+    } catch (Throwable t)
+    {
+      return t;
+    }
+  }
+
+  /**
+   * @j2sAlias parseFeatureFile
+   * 
+   * @param filename
+   * @param alf
+   * @return
+   */
+  @Override
+  public boolean parseFeaturesFile(String filename, AlignFrame alf)
+  {
+    ret[0] = filename;
+    DataSourceType protocol = resolveFileProtocol(ret);
+    if (protocol == null)
+      return false;
+    return (alf == null ? getCurrentAlignFrame() : alf)
+            .parseFeaturesFile(ret[0], protocol);
+  }
+
+  @Override
+  public void removeSelectionListener(String listener, AlignFrame alf)
+  {
+
+    List<SelectionListener> listeners = Desktop
+            .getStructureSelectionManager().getListeners();
+    for (int i = listeners.size(); --i >= 0;)
+    {
+      SelectionListener l = listeners.get(i);
+      if (l instanceof JsSelectionListener
+              && ((JsSelectionListener) l).isFor(alf, listener))
+      {
+        listeners.remove(i);
+        break;
+      }
+    }
+  }
+
+  private DataSourceType resolveFileProtocol(String[] retPath)
+  {
+    String path = retPath[0];
+    /*
+     * is it paste data?
+     */
+    if (path.startsWith("PASTE"))
+    {
+      retPath[0] = path.substring(5);
+      return DataSourceType.PASTE;
+    }
+
+    /*
+     * is it a URL?
+     */
+    if (path.indexOf("://") >= 0)
+    {
+      return DataSourceType.URL;
+    }
+
+    /*
+     * try relative to document root
+     */
+    URL documentBase = getDocumentBase();
+    String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
+    if (HttpUtils.isValidUrl(withDocBase))
+    {
+      // if (debug)
+      // {
+      // System.err.println("Prepended document base '" + documentBase
+      // + "' to make: '" + withDocBase + "'");
+      // }
+      retPath[0] = withDocBase;
+      return DataSourceType.URL;
+    }
+
+    /*
+     * try relative to codebase (if different to document base)
+     */
+    URL codeBase = getCodeBase();
+    String withCodeBase = resolveUrlForLocalOrAbsolute(path, codeBase);
+    if (!withCodeBase.equals(withDocBase)
+            && HttpUtils.isValidUrl(withCodeBase))
+    {
+      // if (debug)
+      // {
+      // System.err.println("Prepended codebase '" + codeBase
+      // + "' to make: '" + withCodeBase + "'");
+      // }
+      retPath[0] = withCodeBase;
+      return DataSourceType.URL;
+    }
+
+    /*
+     * try locating by classloader; try this last so files in the directory
+     * are resolved using document base
+     */
+    if (inArchive(getClass(), path))
+    {
+      return DataSourceType.CLASSLOADER;
+    }
+    return null;
+  }
+
+  @Override
+  public void scrollViewTo(int topRow, int leftHandColumn, AlignFrame alf)
+  {
+    // TODO test
+    java.awt.EventQueue.invokeLater(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        try
+        {
+          (alf == null ? getCurrentAlignFrame() : alf).scrollTo(topRow,
+                  leftHandColumn);
+        } catch (Exception ex)
+        {
+          System.err.println("Couldn't parse integer arguments (topRow='"
+                  + topRow + "' and leftHandColumn='" + leftHandColumn
+                  + "')");
+          ex.printStackTrace();
+        }
+      }
+    });
+  }
+
+  @Override
+  public void select(String ids[], String cols[], AlignFrame alf)
+  {
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    final SequenceGroup sel = new SequenceGroup();
+    final ColumnSelection csel = new ColumnSelection();
+    AlignmentI al = alf.getViewport().getAlignment();
+    jalview.analysis.SequenceIdMatcher matcher = new jalview.analysis.SequenceIdMatcher(
+            alf.getViewport().getAlignment().getSequencesArray());
+    int start = 0, end = al.getWidth(), alw = al.getWidth();
+    boolean seqsfound = true;
+    if (ids != null && ids.length > 0)
+    {
+      seqsfound = false;
+      for (int i = 0; i < ids.length; i++)
+      {
+        if (ids[i].trim().length() == 0)
+        {
+          continue;
+        }
+        SequenceI sq = matcher.findIdMatch(ids[i]);
+        if (sq != null)
+        {
+          seqsfound = true;
+          sel.addSequence(sq, false);
+        }
+      }
+    }
+    boolean inseqpos = false;
+    if (cols != null && cols.length > 0)
+    {
+      boolean seset = false;
+      for (int i = 0; i < cols.length; i++)
+      {
+        String cl = cols[i].trim();
+        if (cl.length() == 0)
+        {
+          continue;
+        }
+        int p;
+        if ((p = cl.indexOf("-")) > -1)
+        {
+          int from = -1, to = -1;
+          try
+          {
+            from = Integer.valueOf(cl.substring(0, p)).intValue();
+            from--;
+          } catch (NumberFormatException ex)
+          {
+            System.err.println(
+                    "ERROR: Couldn't parse first integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          try
+          {
+            to = Integer.valueOf(cl.substring(p + 1)).intValue();
+            to--;
+          } catch (NumberFormatException ex)
+          {
+            System.err.println(
+                    "ERROR: Couldn't parse second integer in range element column selection string '"
+                            + cl + "' - format is 'from-to'");
+            return;
+          }
+          if (from >= 0 && to >= 0)
+          {
+            // valid range
+            if (from < to)
+            {
+              int t = to;
+              to = from;
+              to = t;
+            }
+            if (!seset)
+            {
+              start = from;
+              end = to;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > from)
+              {
+                start = from;
+              }
+              if (end < to)
+              {
+                end = to;
+              }
+            }
+            for (int r = from; r <= to; r++)
+            {
+              if (r >= 0 && r < alw)
+              {
+                csel.addElement(r);
+              }
+            }
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Range '" + cl
+                    + "' deparsed as [" + from + "," + to + "]");
+          }
+        }
+        else
+        {
+          int r = -1;
+          try
+          {
+            r = Integer.valueOf(cl).intValue();
+            r--;
+          } catch (NumberFormatException ex)
+          {
+            if (cl.toLowerCase().equals("sequence"))
+            {
+              // we are in the dataset sequence's coordinate frame.
+              inseqpos = true;
+            }
+            else
+            {
+              System.err.println(
+                      "ERROR: Couldn't parse integer from point selection element of column selection string '"
+                              + cl + "'");
+              return;
+            }
+          }
+          if (r >= 0 && r <= alw)
+          {
+            if (!seset)
+            {
+              start = r;
+              end = r;
+              seset = true;
+            }
+            else
+            {
+              // comment to prevent range extension
+              if (start > r)
+              {
+                start = r;
+              }
+              if (end < r)
+              {
+                end = r;
+              }
+            }
+            csel.addElement(r);
+          }
+          else
+          {
+            System.err.println("ERROR: Invalid Point selection '" + cl
+                    + "' deparsed as [" + r + "]");
+          }
+        }
+      }
+    }
+    if (seqsfound)
+    {
+      // we only propagate the selection when it was the null selection, or the
+      // given sequences were found in the alignment.
+      if (inseqpos && sel.getSize() > 0)
+      {
+        // assume first sequence provides reference frame ?
+        SequenceI rs = sel.getSequenceAt(0);
+        start = rs.findIndex(start);
+        end = rs.findIndex(end);
+        List<Integer> cs = new ArrayList<>(csel.getSelected());
+        csel.clear();
+        for (Integer selectedCol : cs)
+        {
+          csel.addElement(rs.findIndex(selectedCol));
+        }
+      }
+      sel.setStartRes(start);
+      sel.setEndRes(end);
+      AlignFrame af = alf;
+      EventQueue.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          af.select(sel, csel,
+                  af.getCurrentView().getAlignment().getHiddenColumns());
+        }
+      });
+    }
+  }
+
+  //
+  // @Override
+  // public void setFeatureGroupState(String[] groups, boolean state)
+  // {
+  // setFeatureGroupState(null, groups, state);
+  // }
+  //
+  // @Override
+  // public void setFeatureGroupState(String[] groups, boolean state)
+  // { // JalviewLite API
+  // setFeatureGroupStateOn(null, groups, state);
+  // }
+  //
+  @Override
+  public void setFeatureGroupState(final String[] groups,
+          boolean state, AlignFrame alf)
+  {
+    // setFeatureGroupState(alf, groups, state);
+    // java.awt.EventQueue.invokeLater(new Runnable()
+    // {
+    // @Override
+    // public void run()
+    // {
+    // (alf == null ? getCurrentAlignFrame() : alf)
+    // .setFeatureGroupState(
+    // separatorListToArray(groups, separator), state);
+    // }
+    // });
+    // }
+    //
+    // public void setFeatureGroupState(AlignFrame alf, String[] groups, boolean
+    // state) {
+    (alf == null ? getCurrentAlignFrame() : alf)
+            .setFeatureGroupState(groups, state);
+  }
+
+  @Override
+  public void setSelectionListener(String listener, AlignFrame alf)
+  {
+    Desktop.getStructureSelectionManager()
+            .addSelectionListener(new JsSelectionListener(alf, listener));
+  }
+
+  @Override
+  public void showOverview()
+  {
+    getCurrentAlignFrame().overviewMenuItem_actionPerformed(null);
+  }
+
+  /**
+   * @j2sAlias showStructure
+   */
+  @Override
+  public void showStructure(String pdbID, String fileType, AlignFrame alf)
+  {
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    PDBEntry pe = null;
+    SequenceI[] seqs = null;
+    if (pdbID == null)
+    {
+      seqs = alf.getViewport().getSequenceSelection();
+      if (seqs.length == 0)
+        seqs = alf.getViewport().getAlignment().getSequencesArray();
+      for (int i = 0; i < seqs.length; i++)
+      {
+        Vector<PDBEntry> list = seqs[i].getAllPDBEntries();
+        if (list.size() > 0)
+        {
+          pe = list.get(0);
+          break;
+        }
+      }
+    }
+    if (pe == null)
+    {
+      if (pdbID == null)
+        return;
+      pe = new PDBEntry(pdbID, null, fileType);
+      List<SequenceI> list = alf.getViewport().getAlignment()
+              .getSequences();
+      List<SequenceI> tmp = new ArrayList<SequenceI>();
+      for (int i = 0; i < list.size(); i++)
+      {
+        SequenceI seq = list.get(i);
+        if (seq.getPDBEntry(pdbID) != null)
+        {
+          tmp.add(seq);
+        }
+      }
+      seqs = tmp.toArray(new SequenceI[tmp.size()]);
+      alf.alignPanel.selectSequences(tmp);
+    }
+    StructureViewer.launchStructureViewer(alf.alignPanel, pe, seqs);
+  }
+
+  // private or package-private methods
+
+  /**
+   * form a complete URL given a path to a resource and a reference location on
+   * the same server
+   * 
+   * @param targetPath
+   *          - an absolute path on the same server as localref or a document
+   *          located relative to localref
+   * @param localref
+   *          - a URL on the same server as url
+   * @return a complete URL for the resource located by url
+   */
+  private static String resolveUrlForLocalOrAbsolute(String targetPath,
+          URL localref)
+  {
+    String resolvedPath = "";
+    if (targetPath.startsWith("/"))
+    {
+      String codebase = localref.toString();
+      String localfile = localref.getFile();
+      resolvedPath = codebase.substring(0,
+              codebase.length() - localfile.length()) + targetPath;
+      return resolvedPath;
+    }
+
+    /*
+     * get URL path and strip off any trailing file e.g.
+     * www.jalview.org/examples/index.html#applets?a=b is trimmed to
+     * www.jalview.org/examples/
+     */
+    String urlPath = localref.toString();
+    String directoryPath = urlPath;
+    int lastSeparator = directoryPath.lastIndexOf("/");
+    if (lastSeparator > 0)
+    {
+      directoryPath = directoryPath.substring(0, lastSeparator + 1);
+    }
+
+    if (targetPath.startsWith("/"))
+    {
+      /*
+       * construct absolute URL to a file on the server - this is not allowed?
+       */
+      // String localfile = localref.getFile();
+      // resolvedPath = urlPath.substring(0,
+      // urlPath.length() - localfile.length())
+      // + targetPath;
+      resolvedPath = directoryPath + targetPath.substring(1);
+    }
+    else
+    {
+      resolvedPath = directoryPath + targetPath;
+    }
+    // if (debug)
+    // {
+    // System.err.println(
+    // "resolveUrlForLocalOrAbsolute returning " + resolvedPath);
+    // }
+    return resolvedPath;
+  }
+
+  /**
+   * parse the string into a list
+   * 
+   * @param list
+   * @param separator
+   * @return elements separated by separator
+   */
+  private static String[] separatorListToArray(String list,
+          String separator)
+  {
+    // TODO use StringUtils version (slightly different...)
+    int seplen = separator.length();
+    if (list == null || list.equals("") || list.equals(separator))
+    {
+      return null;
+    }
+    Vector<String> jv = new Vector<>();
+    int cp = 0, pos;
+    while ((pos = list.indexOf(separator, cp)) > cp)
+    {
+      jv.addElement(list.substring(cp, pos));
+      cp = pos + seplen;
+    }
+    if (cp < list.length())
+    {
+      String c = list.substring(cp);
+      if (!c.equals(separator))
+      {
+        jv.addElement(c);
+      }
+    }
+    if (jv.size() > 0)
+    {
+      String[] v = new String[jv.size()];
+      for (int i = 0; i < v.length; i++)
+      {
+        v[i] = jv.elementAt(i);
+      }
+      jv.removeAllElements();
+      return v;
+    }
+    return null;
+  }
+
+  /**
+   * Discovers whether the given file is in the Applet Archive
+   * 
+   * @param f
+   *          String
+   * @return boolean
+   */
+  private static boolean inArchive(Class<?> c, String f)
+  {
+    // This might throw a security exception in certain browsers
+    // Netscape Communicator for instance.
+    try
+    {
+      boolean rtn = (c.getResourceAsStream("/" + f) != null);
+      return rtn;
+    } catch (Exception ex)
+    {
+      System.out.println("Exception checking resources: " + f + " " + ex);
+      return false;
+    }
+  }
+
+  /**
+   * Allowing for a JavaScript function here.
+   */
+  void callInitCallback()
+  {
+    Object initjscallback = getParameterAsObject("oninit");
+    if (initjscallback != null)
+    {
+      SwingUtilities.invokeLater(new Runnable() {
+
+        @Override
+        public void run()
+        {
+          try
+          {
+            doSendCallback(initjscallback, new Object[] {this});
+          } catch (Exception e)
+          {
+            System.err.println("Exception when executing _oninit callback '"
+                    + initjscallback + "'.");
+            e.printStackTrace();
+          }
+        }
+        
+      });
+    }
+  }
+
+  /**
+   * Pass the provided array prepended with Jalview.this
+   * 
+   * Appropriated from org.jmol.appletjs.Jmol
+   * 
+   * @param callback
+   *          a window function or "alert"
+   * @param data
+   * @return String return from the callback method.
+   */
+  String doSendCallback(Object callback, Object[] data)
+  {
+    Jalview me = jalview;
+
+    if (me != null && callback != null)
+    {
+      /**
+       * @j2sNative
+       * 
+       *            try{
+       * 
+       *            if (callback == "alert") { alert(data[0]); return ""; } var
+       *            o; if (typeof callback == "function") { o = callback; } else
+       *            { if (!callback)return; var tokens = callback.split("."); o
+       *            = window[tokens[0]]; for (var i = 1; i < tokens.length; i++)
+       *            o = o[tokens[i]]; } var a = [me]; for (var i = 0; i <
+       *            data.length; i++) a.push(data[i] ? data[i].booleanValue &&
+       *            (data[i] = data[i].booleanValue()) : data[i]); return
+       *            o.apply(null,a) } catch (e) { System.out.println(callback +
+       *            " failed " + e); }
+       */
+    }
+    return "";
+  }
+
+  /**
+   * Initialize from Info.key/value pairs that match the old JalviewLite applet
+   * parameters.
+   * 
+   * See http://www.jalview.org/old/v2_8/examples/appletParameters.html
+   * 
+   * Note that some of these parameters are handled as command-line arguments,
+   * as determined in ArgsParser.
+   * 
+   * @param alf
+   */
+  void initFromParams(AlignFrame alf)
+  {
+    String sep = getParameter("separator");
+    if (sep != null && sep.length() > 0)
+    {
+      separator = sep;
+    }
+    initTree(alf);
+    initScoreFile(alf);
+    initFeatures(alf);
+    initAnnotations(alf);
+    initJnetFile(alf);
+    initPdbFiles(alf);
+  }
+
+  /**
+   * Load annotations if specified by parameter. Returns true if loaded, else
+   * false.
+   * 
+   * 
+   * @param alignFrame
+   * @return
+   */
+  private boolean initAnnotations(AlignFrame alf)
+  {
+
+    String param = getParameter("annotations");
+    if (param == null)
+      return false;
+    ret[0] = param;
+    DataSourceType protocol = resolveFileProtocol(ret);
+    param = ret[0];
+    if (!new AnnotationFile().annotateAlignmentView(alf.getViewport(),
+            param, protocol))
+    {
+      System.err.println("Annotations were not added from annotation file '"
+              + param + "'");
+      return false;
+    }
+    updateForAnnotations();
+    return true;
+  }
+
+  /**
+   * Load features file and view settings as specified by parameters. Returns
+   * true if features were loaded, else false.
+   * 
+   * @param
+   * 
+   * @param alignFrame
+   * @return
+   */
+  private boolean initFeatures(AlignFrame alf)
+  {
+
+    // ///////////////////////////
+    // modify display of features
+    // we do this before any features have been loaded, ensuring any hidden
+    // groups are hidden when features first displayed
+    //
+    // hide specific groups
+    //
+    String param = getParameter("hidefeaturegroups");
+    if (param != null)
+    {
+      setFeatureGroupState(separatorListToArray(param, separator),
+              false, alf);
+      // setFeatureGroupStateOn(newAlignFrame, param, false);
+    }
+    // show specific groups
+    param = getParameter("showfeaturegroups");
+    if (param != null)
+    {
+      setFeatureGroupState(separatorListToArray(param, separator),
+              true, alf);
+      // setFeatureGroupStateOn(newAlignFrame, param, true);
+    }
+    // and now load features
+    param = getParameter("features");
+    if (param == null)
+    {
+      return false;
+    }
+    if (!parseFeaturesFile(param, alf))
+      return false;
+    param = getParameter("showFeatureSettings");
+    if (param != null && param.equalsIgnoreCase("true"))
+    {
+      alf.showFeatureSettingsUI();
+    }
+    return true;
+  }
+
+  /**
+   * Load in a Jnetfile if specified by parameter. Returns true if loaded, else
+   * false.
+   * 
+   * @param alignFrame
+   * @return
+   */
+  private boolean initJnetFile(AlignFrame alf)
+  {
+
+    String param = getParameter("jnetfile");
+    if (param == null)
+    {
+      // jnet became jpred around 2016
+      param = getParameter("jpredfile");
+    }
+    if (param != null)
+    {
+      try
+      {
+        ret[0] = param;
+        DataSourceType protocol = resolveFileProtocol(ret);
+        JPredFile predictions = new JPredFile(ret[0], protocol);
+        JnetAnnotationMaker.add_annotation(predictions,
+                alf.getViewport().getAlignment(), 0, false);
+        // false == do not add sequence profile from concise output
+        alf.getViewport().getAlignment().setupJPredAlignment();
+        updateForAnnotations();
+      } catch (Exception ex)
+      {
+        ex.printStackTrace();
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Load PDBFiles if any specified by parameter(s). Returns true if loaded,
+   * else false.
+   * 
+   * @param loaderFrame
+   * @return
+   */
+  private boolean initPdbFiles(AlignFrame alf)
+  {
+
+    /*
+     * <param name="alignpdbfiles" value="false/true"/> Undocumented for 2.6 -
+     * related to JAL-434
+     */
+
+    // not supported (as for JalviewLite)
+    // boolean doAlign = false;//"true".equalsIgnoreCase("" +
+    // getAppletParameter("alignpdbfiles", false));
+    // setAlignPdbStructures(doAlign);
+    /*
+     * <param name="PDBfile" value="1gaq.txt PDB|1GAQ|1GAQ|A PDB|1GAQ|1GAQ|B
+     * PDB|1GAQ|1GAQ|C">
+     * 
+     * <param name="PDBfile2" value="1gaq.txt A=SEQA B=SEQB C=SEQB">
+     * 
+     * <param name="PDBfile3" value="1q0o Q45135_9MICO">
+     */
+
+    // Accumulate pdbs here if they are heading for the same view (if
+    // alignPdbStructures is true)
+    // ArrayList<Object[]> pdbs = new ArrayList<>();
+    // init a lazy matcher if we're asked to
+    boolean relaxed = "true"
+            .equalsIgnoreCase(getParameter("relaxedidmatch"));
+    jalview.analysis.SequenceIdMatcher matcher = relaxed
+            ? new jalview.analysis.SequenceIdMatcher(
+                    alf.getViewport().getAlignment().getSequencesArray())
+            : null;
+
+    String param = getParameter("PDBFILE");
+    int plast = (param == null ? 9 : 1);
+    if (param == null && (param = getParameter("PDBFILE1")) == null)
+    {
+      return false;
+    }
+    for (int p = 1; p <= plast; p++)
+    {
+      if (p > 1)
+      {
+        param = getParameter("PDBFILE" + p);
+        if (param == null)
+          break;
+      }
+      PDBEntry pdb = new PDBEntry();
+
+      String seqstring;
+      SequenceI[] seqs = null;
+      String[] chains = null;
+
+      StringTokenizer st = new StringTokenizer(param, " ");
+
+      if (st.countTokens() < 2)
+      {
+        String sequence = getParameter("PDBSEQ");
+        if (sequence != null)
+        {
+          seqs = new SequenceI[] { matcher == null
+                  ? (Sequence) alf.getViewport().getAlignment()
+                          .findName(sequence)
+                  : matcher.findIdMatch(sequence) };
+        }
+
+      }
+      else
+      {
+        param = st.nextToken();
+        List<SequenceI> tmp = new ArrayList<>();
+        List<String> tmp2 = new ArrayList<>();
+
+        while (st.hasMoreTokens())
+        {
+          seqstring = st.nextToken();
+          StringTokenizer st2 = new StringTokenizer(seqstring, "=");
+          if (st2.countTokens() > 1)
+          {
+            // This is the chain
+            tmp2.add(st2.nextToken());
+            seqstring = st2.nextToken();
+          }
+          tmp.add(matcher == null
+                  ? (Sequence) alf.getViewport().getAlignment()
+                          .findName(seqstring)
+                  : matcher.findIdMatch(seqstring));
+        }
+
+        seqs = tmp.toArray(new SequenceI[tmp.size()]);
+        if (tmp2.size() == tmp.size())
+        {
+          chains = tmp2.toArray(new String[tmp2.size()]);
+        }
+      }
+      pdb.setId(param);
+      ret[0] = param;
+      DataSourceType protocol = resolveFileProtocol(ret);
+      // TODO check JAL-357 for files in a jar (CLASSLOADER)
+      pdb.setFile(ret[0]);
+
+      if (seqs != null)
+      {
+        for (int i = 0; i < seqs.length; i++)
+        {
+          if (seqs[i] != null)
+          {
+            ((Sequence) seqs[i]).addPDBId(pdb);
+            StructureSelectionManager
+                    .getStructureSelectionManager(
+                            (StructureSelectionManagerProvider) this)
+                    .registerPDBEntry(pdb);
+          }
+        }
+
+        // if (doAlign)
+        // {
+        // pdbs.add(new Object[] { pdb, seqs, chains, protocol });
+        // }
+        // else
+        {
+          StructureViewer.launchStructureViewer(
+                  (alf == null ? getCurrentAlignFrame() : alf).alignPanel,
+                  pdb, seqs);
+        }
+      }
+    }
+    //
+    // if (doAlign && pdbs.size() > 0)
+    // {
+    // SequenceI[][] seqs = new SequenceI[pdbs.size()][];
+    // PDBEntry[] pdb = new PDBEntry[pdbs.size()];
+    // String[][] chains = new String[pdbs.size()][];
+    // String[] protocols = new String[pdbs.size()];
+    // for (int pdbsi = 0, pdbsiSize = pdbs
+    // .size(); pdbsi < pdbsiSize; pdbsi++)
+    // {
+    // Object[] o = pdbs.get(pdbsi);
+    // pdb[pdbsi] = (PDBEntry) o[0];
+    // seqs[pdbsi] = (SequenceI[]) o[1];
+    // chains[pdbsi] = (String[]) o[2];
+    // protocols[pdbsi] = (String) o[3];
+    // }
+    //// alignedStructureView(pdb, seqs, chains, protocols);
+    // result = true;
+    // }
+    return true;
+  }
+
+  /**
+   * Load a score file if specified by parameter. Returns true if file was
+   * loaded, else false.
+   * 
+   * @param loaderFrame
+   */
+  private boolean initScoreFile(AlignFrame alf)
+  {
+
+    String sScoreFile = getParameter("scoreFile");
+    if (sScoreFile != null && !"".equals(sScoreFile))
+    {
+      try
+      {
+        if (loadScoreFile(sScoreFile, alf))
+        {
+          return true;
+        }
+        System.err.println(
+                "Failed to parse T-COFFEE parameter as a valid score file ('"
+                        + sScoreFile + "')");
+      } catch (Exception e)
+      {
+        System.err.printf("Cannot read score file: '%s'. Cause: %s \n",
+                sScoreFile, e.getMessage());
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Load a tree for the alignment if specified by parameter. Returns true if a
+   * tree was loaded, else false.
+   * 
+   * @return
+   */
+  private boolean initTree(AlignFrame alf)
+  {
+    String treeFile;
+    if ((treeFile = getParameter("tree")) == null
+            && (treeFile = getParameter("treefile")) == null)
+      return false;
+    if (alf == null)
+      alf = getCurrentAlignFrame();
+    try
+    {
+      ret[0] = treeFile;
+      NewickFile nf = new NewickFile(treeFile, resolveFileProtocol(ret));
+      nf.parse();
+      if (nf.getTree() != null)
+      {
+        treeFile = ret[0];
+        alf.getViewport()
+                .setCurrentTree(alf.showNewickTree(nf, treeFile).getTree());
+        return true;
+      }
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
+    }
+    return false;
+  }
+
+  private void updateForAnnotations()
+  {
+    getCurrentAlignFrame().updateForAnnotations();
+  }
+}
index e7f2a53..6a8db59 100644 (file)
@@ -45,10 +45,12 @@ import jalview.io.IdentifyFile;
 import jalview.io.JPredFile;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
-import jalview.javascript.JSFunctionExec;
-import jalview.javascript.JalviewLiteJsApi;
-import jalview.javascript.JsCallBack;
-import jalview.javascript.MouseOverStructureListener;
+import jalview.appletgui.js.JSFunctionExec;
+import jalview.appletgui.js.JalviewLiteJsApi;
+import jalview.appletgui.js.JsCallBack;
+import jalview.appletgui.js.JsSelectionSender;
+import jalview.appletgui.js.MouseOverListener;
+import jalview.appletgui.js.MouseOverStructureListener;
 import jalview.structure.SelectionListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.ColorUtils;
@@ -920,7 +922,7 @@ public class JalviewLite extends Applet
     setMouseoverListener(currentAlignFrame, listener);
   }
 
-  private Vector<jalview.javascript.JSFunctionExec> javascriptListeners = new Vector<>();
+  private Vector<JSFunctionExec> javascriptListeners = new Vector<>();
 
   /*
    * (non-Javadoc)
@@ -942,7 +944,7 @@ public class JalviewLite extends Applet
         return;
       }
     }
-    jalview.javascript.MouseOverListener mol = new jalview.javascript.MouseOverListener(
+    MouseOverListener mol = new MouseOverListener(
             this, af, listener);
     javascriptListeners.addElement(mol);
     StructureSelectionManager.getStructureSelectionManager(this)
@@ -989,7 +991,7 @@ public class JalviewLite extends Applet
         return;
       }
     }
-    jalview.javascript.JsSelectionSender mol = new jalview.javascript.JsSelectionSender(
+    JsSelectionSender mol = new JsSelectionSender(
             this, af, listener);
     javascriptListeners.addElement(mol);
     StructureSelectionManager.getStructureSelectionManager(this)
@@ -1128,7 +1130,7 @@ public class JalviewLite extends Applet
     {
       while (javascriptListeners.size() > 0)
       {
-        jalview.javascript.JSFunctionExec mol = javascriptListeners
+        JSFunctionExec mol = javascriptListeners
                 .elementAt(0);
         javascriptListeners.removeElement(mol);
         if (mol instanceof SelectionListener)
@@ -1155,7 +1157,7 @@ public class JalviewLite extends Applet
     StructureSelectionManager.release(this);
   }
 
-  private jalview.javascript.JSFunctionExec jsFunctionExec;
+  private JSFunctionExec jsFunctionExec;
 
   /*
    * (non-Javadoc)
@@ -1230,7 +1232,7 @@ public class JalviewLite extends Applet
    * (non-Javadoc)
    * 
    * @see
-   * jalview.javascript.JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
+   * JalviewLiteJsApi#scrollViewToRowIn(jalview.appletgui
    * .AlignFrame, java.lang.String)
    */
   @Override
@@ -1261,7 +1263,7 @@ public class JalviewLite extends Applet
    * (non-Javadoc)
    * 
    * @see
-   * jalview.javascript.JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
+   * JalviewLiteJsApi#scrollViewToColumnIn(jalview.appletgui
    * .AlignFrame, java.lang.String)
    */
   @Override
index 3d81e32..3a79ac6 100755 (executable)
@@ -617,7 +617,8 @@ public class Alignment implements AlignmentI, AutoCloseable
   @Override
   public SequenceI findName(SequenceI startAfter, String token, boolean b)
   {
-
+    if (token == null)
+      return null;
     int i = 0;
     SequenceI sq = null;
     String sqname = null;
index c1dc77c..a7e5afe 100755 (executable)
  */
 package jalview.datamodel;
 
-import jalview.util.CaseInsensitiveString;
-
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Hashtable;
 
+import jalview.util.CaseInsensitiveString;
+
 public class PDBEntry
 {
 
@@ -152,6 +152,32 @@ public class PDBEntry
   {
   }
 
+  /**
+   * Entry point when file is not known and fileType may be string
+   * @param pdbId
+   * @param chain may be null
+   * @param fileType "pdb", "mmcif", or "bcif"; null defaults to mmcif
+   */
+  public PDBEntry(String pdbId, String chain, String fileType) {
+    this.id = pdbId.toLowerCase();
+    setChainCode(chain); // I note that PDB Chains ARE case-sensitive now
+    if (fileType == null)
+      fileType = "mmcif";
+    switch (fileType.toLowerCase()) {
+    case "pdb":
+      this.type = Type.PDB.toString();
+      break;
+    case "mmcif":
+      this.type = Type.MMCIF.toString();
+      break;
+    default:
+    case "bcif":
+      System.out.println("format " + fileType + " has not been implemented; using mmCIF");
+      this.type = Type.MMCIF.toString();
+      break;
+    }
+  }
+  
   public PDBEntry(String pdbId, String chain, PDBEntry.Type type,
           String filePath)
   {
@@ -253,6 +279,12 @@ public class PDBEntry
     return id;
   }
 
+  /**
+   * TODO 
+   * 
+   * @param key  "protocol" 
+   * @param value
+   */
   public void setProperty(String key, Object value)
   {
     if (this.properties == null)
index bcf404b..a57fd55 100644 (file)
@@ -29,17 +29,29 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TreeMap;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A singleton class to hold the set of attributes known for each feature type
  */
-public class FeatureAttributes
+public class FeatureAttributes implements ApplicationSingletonI
 {
   public enum Datatype
   {
     Character, Number, Mixed
   }
 
-  private static FeatureAttributes instance = new FeatureAttributes();
+  public static FeatureAttributes getInstance()
+  {
+    return (FeatureAttributes) ApplicationSingletonProvider
+            .getInstance(FeatureAttributes.class);
+  }
+
+  private FeatureAttributes()
+  {
+    attributes = new HashMap<>();
+  }
 
   /*
    * map, by feature type, of a map, by attribute name, of
@@ -120,12 +132,17 @@ public class FeatureAttributes
       if (value != null)
       {
         value = value.trim();
+        if (value.isEmpty())
+        {
+          return;
+        }
 
         /*
          * Parse numeric value unless we have previously
          * seen text data for this attribute type
          */
-        if (type == null || type == Datatype.Number)
+        if ((type == null && couldBeNumber(value))
+                || type == Datatype.Number)
         {
           try
           {
@@ -149,11 +166,23 @@ public class FeatureAttributes
             hasValue = false;
           }
         }
+        else
+        {
+          /*
+           * if not a number, and not seen before...
+           */
+          type = Datatype.Character;
+          min = 0f;
+          max = 0f;
+          hasValue = false;
+        }
       }
     }
 
     /**
-     * Answers the description of the attribute, if recorded and unique, or null if either no, or more than description is recorded
+     * Answers the description of the attribute, if recorded and unique, or null
+     * if either no, or more than description is recorded
+     * 
      * @return
      */
     public String getDescription()
@@ -193,21 +222,6 @@ public class FeatureAttributes
   }
 
   /**
-   * Answers the singleton instance of this class
-   * 
-   * @return
-   */
-  public static FeatureAttributes getInstance()
-  {
-    return instance;
-  }
-
-  private FeatureAttributes()
-  {
-    attributes = new HashMap<>();
-  }
-
-  /**
    * Answers the attribute names known for the given feature type, in
    * alphabetical order (not case sensitive), or an empty set if no attributes
    * are known. An attribute name is typically 'simple' e.g. "AC", but may be
@@ -227,6 +241,34 @@ public class FeatureAttributes
   }
 
   /**
+   * A partial check that the string is numeric - only checks the first
+   * character. Returns true if the first character is a digit, or if it is '.',
+   * '+' or '-' and not the only character. Otherwise returns false (including
+   * for an empty string). Note this is not a complete check as it returns true
+   * for (e.g.) "1A".
+   * 
+   * @param f
+   * @return
+   */
+  public static boolean couldBeNumber(String f)
+  {
+    int len = f.length();
+    if (len == 0)
+    {
+      return false;
+    }
+    char ch = f.charAt(0);
+    switch (ch)
+    {
+    case '.':
+    case '+':
+    case '-':
+      return len > 1;
+    }
+    return (ch <= '9' && ch >= '0');
+  }
+
+  /**
    * Answers true if at least one attribute is known for the given feature type,
    * else false
    * 
@@ -354,7 +396,7 @@ public class FeatureAttributes
     {
       return;
     }
-  
+
     Map<String[], AttributeData> atts = attributes.get(featureType);
     if (atts == null)
     {
index b316821..4a87349 100644 (file)
@@ -23,6 +23,9 @@ package jalview.datamodel.features;
 import java.util.HashMap;
 import java.util.Map;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A singleton to hold metadata about feature attributes, keyed by a unique
  * feature source identifier
@@ -30,22 +33,17 @@ import java.util.Map;
  * @author gmcarstairs
  *
  */
-public class FeatureSources
+public class FeatureSources implements ApplicationSingletonI
 {
-  private static FeatureSources instance = new FeatureSources();
-
-  private Map<String, FeatureSourceI> sources;
 
-  /**
-   * Answers the singleton instance of this class
-   * 
-   * @return
-   */
   public static FeatureSources getInstance()
   {
-    return instance;
+    return (FeatureSources) ApplicationSingletonProvider
+            .getInstance(FeatureSources.class);
   }
 
+  private Map<String, FeatureSourceI> sources;
+
   private FeatureSources()
   {
     sources = new HashMap<>();
index 54c0d59..7651016 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.datamodel.features;
 
-import jalview.datamodel.SequenceFeature;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -30,7 +28,9 @@ import java.util.Set;
 
 import intervalstore.api.IntervalStoreI;
 import intervalstore.impl.BinarySearcher;
+import intervalstore.impl.BinarySearcher.Compare;
 import intervalstore.impl.IntervalStore;
+import jalview.datamodel.SequenceFeature;
 
 /**
  * A data store for a set of sequence features that supports efficient lookup of
@@ -277,7 +277,7 @@ public class FeatureStore
      * binary search the sorted list to find the insertion point
      */
     int insertPosition = BinarySearcher.findFirst(contactFeatureStarts,
-            f -> f.getBegin() >= feature.getBegin());
+            true, Compare.GE, feature.getBegin());
     contactFeatureStarts.add(insertPosition, feature);
 
 
@@ -286,7 +286,7 @@ public class FeatureStore
      * binary search the sorted list to find the insertion point
      */
     insertPosition = BinarySearcher.findFirst(contactFeatureEnds,
-            f -> f.getEnd() >= feature.getEnd());
+            false, Compare.GE, feature.getEnd());
     contactFeatureEnds.add(insertPosition, feature);
 
     return true;
@@ -314,8 +314,8 @@ public class FeatureStore
      */
     // int pos = binarySearch(features,
     // SearchCriterion.byFeature(feature, RangeComparator.BY_START_POSITION));
-    int pos = BinarySearcher.findFirst(features,
-            val -> val.getBegin() >= feature.getBegin());
+    int pos = BinarySearcher.findFirst(features, true, Compare.GE,
+            feature.getBegin());
     int len = features.size();
     while (pos < len)
     {
@@ -396,7 +396,7 @@ public class FeatureStore
      * whose end point is not before the target range
      */
     int index = BinarySearcher.findFirst(contactFeatureEnds,
-            f -> f.getEnd() >= from);
+            false, Compare.GE, (int) from);
 
     while (index < contactFeatureEnds.size())
     {
@@ -449,7 +449,7 @@ public class FeatureStore
           List<SequenceFeature> result)
   {
     int index = BinarySearcher.findFirst(contactFeatureStarts,
-            f -> f.getBegin() >= from);
+            true, Compare.GE, (int) from);
 
     while (index < contactFeatureStarts.size())
     {
index db2f0e1..82772bc 100644 (file)
  */
 package jalview.datamodel.features;
 
-import jalview.datamodel.SequenceFeature;
-import jalview.io.gff.SequenceOntologyFactory;
-import jalview.io.gff.SequenceOntologyI;
-
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -33,6 +30,9 @@ import java.util.Set;
 import java.util.TreeMap;
 
 import intervalstore.api.IntervalI;
+import jalview.datamodel.SequenceFeature;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
 
 /**
  * A class that stores sequence features in a way that supports efficient
@@ -44,7 +44,6 @@ import intervalstore.api.IntervalI;
  */
 public class SequenceFeatures implements SequenceFeaturesI
 {
-
   /*
    * map from feature type to structured store of features for that type
    * null types are permitted (but not a good idea!)
@@ -373,7 +372,7 @@ public class SequenceFeatures implements SequenceFeaturesI
     {
       return true;
     }
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     for (String term : soTerm)
     {
       if (type.equals(term) || so.isA(type, term))
@@ -414,7 +413,10 @@ public class SequenceFeatures implements SequenceFeaturesI
   public static void sortFeatures(List<? extends IntervalI> features,
           final boolean forwardStrand)
   {
-    IntervalI.sortIntervals(features, forwardStrand);
+    Collections.sort(features,
+            forwardStrand
+                    ? IntervalI.COMPARE_BEGIN_ASC_END_DESC
+                    : IntervalI.COMPARE_END_DESC);
   }
 
   /**
index e01ad17..ec7318f 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ext.ensembl;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.io.gff.SequenceOntologyI;
+import jalview.util.Platform;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -44,7 +45,7 @@ public class EnsemblCdna extends EnsemblSeqProxy
    * or ENSMUST or similar for other species
    * or CCDSnnnnn.nn with at least 3 digits
    */
-  private static final Regex ACCESSION_REGEX = new Regex(
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(
           "(ENS([A-Z]{3}|)[TG][0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
 
   /*
index 8f13d99..bf37265 100644 (file)
@@ -93,7 +93,7 @@ public class EnsemblCds extends EnsemblSeqProxy
   @Override
   protected boolean retainFeature(SequenceFeature sf, String accessionId)
   {
-    if (SequenceOntologyFactory.getInstance().isA(sf.getType(),
+    if (SequenceOntologyFactory.getSequenceOntology().isA(sf.getType(),
             SequenceOntologyI.CDS))
     {
       return false;
index 4d6c1a9..ab35829 100644 (file)
  */
 package jalview.ext.ensembl;
 
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.io.gff.SequenceOntologyI;
-import jalview.util.JSONUtils;
-
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -38,6 +30,14 @@ import java.util.Map;
 
 import org.json.simple.parser.ParseException;
 
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.util.JSONUtils;
+
 /**
  * A client for fetching and processing Ensembl feature data in GFF format by
  * calling the overlap REST service
index 0e3d84b..3d957f0 100644 (file)
@@ -55,7 +55,7 @@ public class EnsemblGene extends EnsemblSeqProxy
    * accepts anything as we will attempt lookup of gene or 
    * transcript id or gene name
    */
-  private static final Regex ACCESSION_REGEX = new Regex(".*");
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(".*");
 
   private static final EnsemblFeatureType[] FEATURES_TO_FETCH = {
       EnsemblFeatureType.gene, EnsemblFeatureType.transcript,
@@ -578,7 +578,7 @@ public class EnsemblGene extends EnsemblSeqProxy
   @Override
   protected boolean retainFeature(SequenceFeature sf, String accessionId)
   {
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     String type = sf.getType();
     if (so.isA(type, SequenceOntologyI.GENE))
     {
@@ -625,7 +625,7 @@ public class EnsemblGene extends EnsemblSeqProxy
   {
     return new FeatureSettingsAdapter()
     {
-      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+      SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
 
       @Override
       public boolean isFeatureDisplayed(String type)
index 895598f..85f421d 100644 (file)
@@ -20,9 +20,6 @@
  */
 package jalview.ext.ensembl;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefSource;
-
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -34,6 +31,9 @@ import java.util.Set;
 
 import org.json.simple.parser.ParseException;
 
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefSource;
+
 public class EnsemblInfo extends EnsemblRestClient
 {
 
index 0280f16..468f05d 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ext.ensembl;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.util.Platform;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -42,7 +43,7 @@ public class EnsemblProtein extends EnsemblSeqProxy
    * or ENSMUSP or similar for other species
    * or CCDSnnnnn.nn with at least 3 digits
    */
-  private static final Regex ACCESSION_REGEX = new Regex(
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(
           "(ENS([A-Z]{3}|)P[0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
 
   /**
index fd8800f..11f8b7b 100644 (file)
@@ -732,7 +732,7 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
        * for sequence_variant on reverse strand, have to convert the allele
        * values to their complements
        */
-      if (!forwardStrand && SequenceOntologyFactory.getInstance()
+      if (!forwardStrand && SequenceOntologyFactory.getSequenceOntology()
               .isA(sf.getType(), SequenceOntologyI.SEQUENCE_VARIANT))
       {
         reverseComplementAlleles(copy);
@@ -966,7 +966,7 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
   public static boolean isTranscript(String featureType)
   {
     return SequenceOntologyI.NMD_TRANSCRIPT_VARIANT.equals(featureType)
-            || SequenceOntologyFactory.getInstance().isA(featureType,
+            || SequenceOntologyFactory.getSequenceOntology().isA(featureType,
                     SequenceOntologyI.TRANSCRIPT);
   }
 }
index 7454eb6..2008fb6 100644 (file)
@@ -23,6 +23,7 @@ package jalview.ext.ensembl;
 import jalview.analysis.AlignmentUtils;
 import jalview.bin.Cache;
 import jalview.datamodel.DBRefSource;
+import jalview.util.Platform;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 
 import com.stevesoft.pat.Regex;
@@ -50,7 +51,7 @@ abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
    * or ENSMUSP or similar for other species
    * or CCDSnnnnn.nn with at least 3 digits
    */
-  private static final Regex ACCESSION_REGEX = new Regex(
+  private static final Regex ACCESSION_REGEX = Platform.newRegex(
           "(ENS([A-Z]{3}|)[GTEP]{1}[0-9]{11}$)" + "|"
                   + "(CCDS[0-9.]{3,}$)");
 
index 27ca642..9b58e58 100644 (file)
  */
 package jalview.ext.ensembl;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
-import jalview.util.DBRefUtils;
-
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -34,6 +30,10 @@ import java.util.Map;
 
 import org.json.simple.parser.ParseException;
 
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.util.DBRefUtils;
+
 /**
  * A class to fetch cross-references from Ensembl by calling the /xrefs REST
  * service
index 453152e..cfb30b3 100644 (file)
@@ -1368,7 +1368,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
             htmlName + ((Object) this).toString(), documentBase, codeBase,
             commandOptions, this);
 
-    viewer.setJmolStatusListener(this); // extends JmolCallbackListener
+    viewer.setJmolStatusListener(this); 
+    
+    // BH how about extending Jmol's status listener? 
 
     try
     {
index 0d631e6..89b2415 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.ext.so;
 
-import jalview.io.gff.SequenceOntologyI;
-
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -44,6 +42,9 @@ import org.biojava.nbio.ontology.Triple;
 import org.biojava.nbio.ontology.io.OboParser;
 import org.biojava.nbio.ontology.utils.Annotation;
 
+import jalview.bin.Cache;
+import jalview.io.gff.SequenceOntologyI;
+
 /**
  * A wrapper class that parses the Sequence Ontology and exposes useful access
  * methods. This version uses the BioJava parser.
@@ -183,19 +184,19 @@ public class SequenceOntology implements SequenceOntologyI
             boolean oldTermIsObsolete = isObsolete(replaced);
             if (newTermIsObsolete && !oldTermIsObsolete)
             {
-              System.err.println("Ignoring " + term.getName()
+              Cache.log.debug("Ignoring " + term.getName()
                       + " as obsolete and duplicated by "
                       + replaced.getName());
               term = replaced;
             }
             else if (!newTermIsObsolete && oldTermIsObsolete)
             {
-              System.err.println("Ignoring " + replaced.getName()
+              Cache.log.debug("Ignoring " + replaced.getName()
                       + " as obsolete and duplicated by " + term.getName());
             }
             else
             {
-              System.err.println("Warning: " + term.getName()
+              Cache.log.debug("Warning: " + term.getName()
                       + " has replaced " + replaced.getName()
                       + " for lookup of '" + description + "'");
             }
index 22ed591..c6860cc 100644 (file)
@@ -37,6 +37,8 @@ import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.api.client.config.DefaultClientConfig;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.SequenceI;
 import jalview.fts.api.FTSData;
 import jalview.fts.api.FTSDataColumnI;
@@ -53,13 +55,16 @@ import jalview.util.Platform;
  * 
  * @author tcnofoegbu
  */
-public class PDBFTSRestClient extends FTSRestClient
+public class PDBFTSRestClient extends FTSRestClient implements ApplicationSingletonI
 {
-
-  private static FTSRestClientI instance = null;
-
   public static final String PDB_SEARCH_ENDPOINT = "https://www.ebi.ac.uk/pdbe/search/pdb/select?";
 
+  public static FTSRestClientI getInstance()
+  {
+    return (FTSRestClientI) ApplicationSingletonProvider
+            .getInstance(PDBFTSRestClient.class);
+  }
+
   protected PDBFTSRestClient()
   {
   }
@@ -458,15 +463,6 @@ public static String parseJsonExceptionString(String jsonErrorResponse)
     return "/fts/pdb_data_columns.txt";
   }
 
-  public static FTSRestClientI getInstance()
-  {
-    if (instance == null)
-    {
-      instance = new PDBFTSRestClient();
-    }
-    return instance;
-  }
-
   private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
 
   public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
index 3f0b8a4..0d860c4 100644 (file)
@@ -21,6 +21,8 @@
 
 package jalview.fts.service.uniprot;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 import jalview.fts.api.FTSData;
 import jalview.fts.api.FTSDataColumnI;
@@ -44,7 +46,15 @@ import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.api.client.config.DefaultClientConfig;
 
 public class UniProtFTSRestClient extends FTSRestClient
+implements ApplicationSingletonI
 {
+
+public static FTSRestClientI getInstance()
+{
+return (FTSRestClientI) ApplicationSingletonProvider
+    .getInstance(UniProtFTSRestClient.class);
+}
+
   private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
 
   static
@@ -52,11 +62,9 @@ public class UniProtFTSRestClient extends FTSRestClient
     Platform.addJ2SDirectDatabaseCall(DEFAULT_UNIPROT_DOMAIN);
   }
 
-  private static FTSRestClientI instance = null;
-
   public final String uniprotSearchEndpoint;
 
-  public UniProtFTSRestClient()
+  private UniProtFTSRestClient()
   {
     super();
     uniprotSearchEndpoint = Cache.getDefault("UNIPROT_DOMAIN",
@@ -347,15 +355,6 @@ public class UniProtFTSRestClient extends FTSRestClient
     };
   }
 
-  public static FTSRestClientI getInstance()
-  {
-    if (instance == null)
-    {
-      instance = new UniProtFTSRestClient();
-    }
-    return instance;
-  }
-
   @Override
   public String getColumnDataConfigFileName()
   {
index 70601c9..3a8fb7c 100644 (file)
@@ -96,7 +96,7 @@ public class AlignExportOptions extends JPanel
     this.settings = defaults;
     this.isComplexAlignFile = format.isComplexAlignFile();
     init(viewport.hasHiddenRows(), viewport.hasHiddenColumns());
-    dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+    dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane());
   }
 
   /**
index c30a5a9..9d8407a 100644 (file)
  */
 package jalview.gui;
 
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-import java.awt.dnd.DnDConstants;
-import java.awt.dnd.DropTargetDragEvent;
-import java.awt.dnd.DropTargetDropEvent;
-import java.awt.dnd.DropTargetEvent;
-import java.awt.dnd.DropTargetListener;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseEvent;
-import java.awt.print.PageFormat;
-import java.awt.print.PrinterJob;
-import java.beans.PropertyChangeListener;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.PrintWriter;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.List;
-import java.util.Vector;
-
-import javax.swing.ButtonGroup;
-import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JComponent;
-import javax.swing.JEditorPane;
-import javax.swing.JInternalFrame;
-import javax.swing.JLabel;
-import javax.swing.JLayeredPane;
-import javax.swing.JMenu;
-import javax.swing.JMenuItem;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
-import javax.swing.event.InternalFrameAdapter;
-import javax.swing.event.InternalFrameEvent;
-
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.CrossRef;
@@ -175,6 +123,64 @@ import java.util.Set;
 import javax.swing.JFileChooser;
 import javax.swing.JOptionPane;
 
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.print.PageFormat;
+import java.awt.print.PrinterJob;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+
+import ext.vamsas.ServiceHandle;
+
 /**
  * DOCUMENT ME!
  * 
@@ -186,6 +192,9 @@ public class AlignFrame extends GAlignFrame
         implements DropTargetListener, IProgressIndicator,
         AlignViewControllerGuiI, ColourChangeListener, ServiceChangeListener
 {
+
+  public static int frameCount;
+
   public static final int DEFAULT_WIDTH = 700;
 
   public static final int DEFAULT_HEIGHT = 500;
@@ -216,6 +225,10 @@ public class AlignFrame extends GAlignFrame
    */
   File fileObject;
 
+  private int id;
+
+  private DataSourceType protocol ;
+
   /**
    * Creates a new AlignFrame object with specific width and height.
    * 
@@ -310,6 +323,9 @@ public class AlignFrame extends GAlignFrame
   public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
           int height, String sequenceSetId, String viewId)
   {
+
+    id = (++frameCount);
+
     setSize(width, height);
 
     if (al.getDataset() == null)
@@ -319,9 +335,9 @@ public class AlignFrame extends GAlignFrame
 
     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
 
-    alignPanel = new AlignmentPanel(this, viewport);
-
-    addAlignmentPanel(alignPanel, true);
+    // JalviewJS needs to distinguish a new panel from an old one in init()
+    // alignPanel = new AlignmentPanel(this, viewport);
+    // addAlignmentPanel(alignPanel, true);
     init();
   }
 
@@ -341,8 +357,8 @@ public class AlignFrame extends GAlignFrame
     {
       viewport.hideSequence(hiddenSeqs);
     }
-    alignPanel = new AlignmentPanel(this, viewport);
-    addAlignmentPanel(alignPanel, true);
+    // alignPanel = new AlignmentPanel(this, viewport);
+    // addAlignmentPanel(alignPanel, true);
     init();
   }
 
@@ -358,7 +374,7 @@ public class AlignFrame extends GAlignFrame
   {
     viewport = ap.av;
     alignPanel = ap;
-    addAlignmentPanel(ap, false);
+    // addAlignmentPanel(ap, false);
     init();
   }
 
@@ -366,13 +382,38 @@ public class AlignFrame extends GAlignFrame
    * initalise the alignframe from the underlying viewport data and the
    * configurations
    */
+
   void init()
   {
+    boolean newPanel = (alignPanel == null);
+    viewport.setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+    if (newPanel)
+    {
+      if (Platform.isJS())
+      {
+        // need to set this up front if NOANNOTATION is
+        // used in conjunction with SHOWOVERVIEW.
+
+        // I have not determined if this is appropriate for
+        // Jalview/Java, as it means we are setting this flag
+        // for all subsequent AlignFrames. For now, at least,
+        // I am setting it to be JalviewJS-only.
+
+        boolean showAnnotation = Jalview.getInstance().getShowAnnotation();
+        viewport.setShowAnnotation(showAnnotation);
+      }
+      alignPanel = new AlignmentPanel(this, viewport);
+    }
+    addAlignmentPanel(alignPanel, newPanel);
+
     // setBackground(Color.white); // BH 2019
 
     if (!Jalview.isHeadlessMode())
     {
       progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+      // JalviewJS options
+      statusPanel.setVisible(Jalview.getInstance().getShowStatus());
+      alignFrameMenuBar.setVisible(Jalview.getInstance().getAllowMenuBar());
     }
 
     avc = new jalview.controller.AlignViewController(this, viewport,
@@ -387,7 +428,7 @@ public class AlignFrame extends GAlignFrame
       // modifyPID.setEnabled(false);
     }
 
-    String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT",
+    String sortby = jalview.bin.Cache.getDefault(Preferences.SORT_ALIGNMENT,
             "No sort");
 
     if (sortby.equals("Id"))
@@ -399,8 +440,10 @@ public class AlignFrame extends GAlignFrame
       sortPairwiseMenuItem_actionPerformed(null);
     }
 
-    this.alignPanel.av
-            .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+    // BH see above
+    //
+    // this.alignPanel.av
+    // .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
 
     setMenusFromViewport(viewport);
     buildSortByAnnotationScoresMenu();
@@ -415,7 +458,7 @@ public class AlignFrame extends GAlignFrame
     });
     buildColourMenu();
 
-    if (Desktop.desktop != null)
+    if (Desktop.getDesktopPane() != null)
     {
       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
       addServiceListeners();
@@ -430,7 +473,7 @@ public class AlignFrame extends GAlignFrame
       wrapMenuItem_actionPerformed(null);
     }
 
-    if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false))
+    if (jalview.bin.Cache.getDefault(Preferences.SHOW_OVERVIEW, false))
     {
       this.overviewMenuItem_actionPerformed(null);
     }
@@ -514,6 +557,7 @@ public class AlignFrame extends GAlignFrame
     }
     addFocusListener(new FocusAdapter()
     {
+
       @Override
       public void focusGained(FocusEvent e)
       {
@@ -532,6 +576,8 @@ public class AlignFrame extends GAlignFrame
    * @param format
    *          format of file
    */
+
+  @Deprecated
   public void setFileName(String file, FileFormatI format)
   {
     fileName = file;
@@ -540,11 +586,28 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
+   * 
+   * @param fileName
+   * @param file  from SwingJS; may contain bytes -- for reload
+   * @param protocol from SwingJS; may be RELATIVE_URL
+   * @param format
+   */
+  public void setFile(String fileName, File file, DataSourceType protocol, FileFormatI format)
+  {
+    this.fileName = fileName;
+    this.fileObject = file;
+    this.protocol = protocol;
+    setFileFormat(format);
+    reload.setEnabled(true);
+  }
+
+  /**
    * JavaScript will have this, maybe others. More dependable than a file name
    * and maintains a reference to the actual bytes loaded.
    * 
    * @param file
    */
+
   public void setFileObject(File file)
   {
     this.fileObject = file;
@@ -554,10 +617,12 @@ public class AlignFrame extends GAlignFrame
    * Add a KeyListener with handlers for various KeyPressed and KeyReleased
    * events
    */
+
   void addKeyListener()
   {
     addKeyListener(new KeyAdapter()
     {
+
       @Override
       public void keyPressed(KeyEvent evt)
       {
@@ -574,8 +639,9 @@ public class AlignFrame extends GAlignFrame
         switch (evt.getKeyCode())
         {
 
-        case 27: // escape key
-          deselectAllSequenceMenuItem_actionPerformed(null);
+        case KeyEvent.VK_ESCAPE: // escape key
+                                 // alignPanel.deselectAllSequences();
+          alignPanel.deselectAllSequences();
 
           break;
 
@@ -758,16 +824,14 @@ public class AlignFrame extends GAlignFrame
         case KeyEvent.VK_LEFT:
           if (evt.isAltDown() || !viewport.cursorMode)
           {
-            viewport.firePropertyChange("alignment", null,
-                    viewport.getAlignment().getSequences());
+            viewport.notifyAlignment();
           }
           break;
 
         case KeyEvent.VK_RIGHT:
           if (evt.isAltDown() || !viewport.cursorMode)
           {
-            viewport.firePropertyChange("alignment", null,
-                    viewport.getAlignment().getSequences());
+            viewport.notifyAlignment();
           }
           break;
         }
@@ -813,10 +877,13 @@ public class AlignFrame extends GAlignFrame
       {
         ap.av.getAlignment().padGaps();
       }
-      ap.av.updateConservation(ap);
-      ap.av.updateConsensus(ap);
-      ap.av.updateStrucConsensus(ap);
-      ap.av.initInformationWorker(ap);
+      if (Jalview.getInstance().getStartCalculations())
+      {
+        ap.av.updateConservation(ap);
+        ap.av.updateConsensus(ap);
+        ap.av.updateStrucConsensus(ap);
+        ap.av.initInformationWorker(ap);
+      }
     }
   }
 
@@ -852,22 +919,22 @@ public class AlignFrame extends GAlignFrame
     }
     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
     {
-      WSDiscovererI discoverer = Jws2Discoverer.getDiscoverer();
+      WSDiscovererI discoverer = Jws2Discoverer.getInstance();
       discoverer.addServiceChangeListener(this);
     }
     // legacy event listener for compatibility with jws1
     PropertyChangeListener legacyListener = (changeEvent) -> {
       buildWebServicesMenu();
     };
-    Desktop.instance.addJalviewPropertyChangeListener("services",legacyListener);
+    Desktop.getInstance().addJalviewPropertyChangeListener("services",legacyListener);
     
     addInternalFrameListener(new InternalFrameAdapter() {
       @Override
       public void internalFrameClosed(InternalFrameEvent e) {
         System.out.println("deregistering discoverer listener");
         SlivkaWSDiscoverer.getInstance().removeServiceChangeListener(AlignFrame.this);
-        Jws2Discoverer.getDiscoverer().removeServiceChangeListener(AlignFrame.this);
-        Desktop.instance.removeJalviewPropertyChangeListener("services", legacyListener);
+        Jws2Discoverer.getInstance().removeServiceChangeListener(AlignFrame.this);
+        Desktop.getInstance().removeJalviewPropertyChangeListener("services", legacyListener);
         closeMenuItem_actionPerformed(true);
       }
     });
@@ -878,6 +945,7 @@ public class AlignFrame extends GAlignFrame
    * Configure menu items that vary according to whether the alignment is
    * nucleotide or protein
    */
+
   public void setGUINucleotide()
   {
     AlignmentI al = getViewport().getAlignment();
@@ -902,6 +970,7 @@ public class AlignFrame extends GAlignFrame
    * operation that affects the data in the current view (selection changed,
    * etc) to update the menus to reflect the new state.
    */
+
   @Override
   public void setMenusForViewport()
   {
@@ -915,6 +984,7 @@ public class AlignFrame extends GAlignFrame
    * @param av
    *          AlignViewport
    */
+
   public void setMenusFromViewport(AlignViewport av)
   {
     padGapsMenuitem.setSelected(av.isPadGaps());
@@ -932,13 +1002,8 @@ public class AlignFrame extends GAlignFrame
     scaleLeft.setVisible(av.getWrapAlignment());
     scaleRight.setVisible(av.getWrapAlignment());
     annotationPanelMenuItem.setState(av.isShowAnnotation());
-    /*
-     * Show/hide annotations only enabled if annotation panel is shown
-     */
-    showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
-    hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    // Show/hide annotations only enabled if annotation panel is shown
+    syncAnnotationMenuItems(av.isShowAnnotation());
     viewBoxesMenuItem.setSelected(av.getShowBoxes());
     viewTextMenuItem.setSelected(av.getShowText());
     showNonconservedMenuItem.setSelected(av.getShowUnconserved());
@@ -959,7 +1024,8 @@ public class AlignFrame extends GAlignFrame
     applyToAllGroups.setState(av.getColourAppliesToAllGroups());
     showNpFeatsMenuitem.setSelected(av.isShowNPFeats());
     showDbRefsMenuitem.setSelected(av.isShowDBRefs());
-    autoCalculate.setSelected(av.autoCalculateConsensus);
+    autoCalculate
+            .setSelected(av.getAutoCalculateConsensusAndConservation());
     sortByTree.setSelected(av.sortByTree);
     listenToViewSelections.setSelected(av.followSelection);
 
@@ -974,6 +1040,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param b
    */
+
   public void setGroovyEnabled(boolean b)
   {
     runGroovy.setEnabled(b);
@@ -986,6 +1053,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
    */
+
   @Override
   public void setProgressBar(String message, long id)
   {
@@ -1009,6 +1077,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @return true if any progress bars are still active
    */
+
   @Override
   public boolean operationInProgress()
   {
@@ -1020,6 +1089,7 @@ public class AlignFrame extends GAlignFrame
    * will cause the status bar to be hidden, with possibly undesirable flicker
    * of the screen layout.
    */
+
   @Override
   public void setStatus(String text)
   {
@@ -1029,6 +1099,7 @@ public class AlignFrame extends GAlignFrame
   /*
    * Added so Castor Mapping file can obtain Jalview Version
    */
+
   public String getVersion()
   {
     return jalview.bin.Cache.getProperty("VERSION");
@@ -1048,7 +1119,7 @@ public class AlignFrame extends GAlignFrame
   @Override
   public void addFromFile_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport);
+    Desktop.getInstance().inputLocalFileMenuItem_actionPerformed(viewport);
   }
 
   @Override
@@ -1305,97 +1376,96 @@ public class AlignFrame extends GAlignFrame
   @Override
   public void reload_actionPerformed(ActionEvent e)
   {
-    if (fileName != null)
+    if (fileName == null && fileObject == null)
+    {
+      return;
+    }
+    // TODO: JAL-1108 - ensure all associated frames are closed regardless of
+    // originating file's format
+    // TODO: work out how to recover feature settings for correct view(s) when
+    // file is reloaded.
+    if (FileFormat.Jalview.equals(currentFileFormat))
     {
-      // TODO: JAL-1108 - ensure all associated frames are closed regardless of
-      // originating file's format
-      // TODO: work out how to recover feature settings for correct view(s) when
-      // file is reloaded.
-      if (FileFormat.Jalview.equals(currentFileFormat))
+      JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
+      for (int i = 0; i < frames.length; i++)
       {
-        JInternalFrame[] frames = Desktop.desktop.getAllFrames();
-        for (int i = 0; i < frames.length; i++)
+        if (frames[i] instanceof AlignFrame && frames[i] != this
+                && ((AlignFrame) frames[i]).fileName != null
+                && ((AlignFrame) frames[i]).fileName.equals(fileName))
         {
-          if (frames[i] instanceof AlignFrame && frames[i] != this
-                  && ((AlignFrame) frames[i]).fileName != null
-                  && ((AlignFrame) frames[i]).fileName.equals(fileName))
+          try
+          {
+            frames[i].setSelected(true);
+            Desktop.getInstance().closeAssociatedWindows();
+          } catch (java.beans.PropertyVetoException ex)
           {
-            try
-            {
-              frames[i].setSelected(true);
-              Desktop.instance.closeAssociatedWindows();
-            } catch (java.beans.PropertyVetoException ex)
-            {
-            }
           }
-
         }
-        Desktop.instance.closeAssociatedWindows();
 
-        FileLoader loader = new FileLoader();
-        DataSourceType protocol = fileName.startsWith("http:")
-                ? DataSourceType.URL
-                : DataSourceType.FILE;
-        loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
       }
-      else
-      {
-        Rectangle bounds = this.getBounds();
+      Desktop.getInstance().closeAssociatedWindows();
 
-        FileLoader loader = new FileLoader();
+      FileLoader loader = new FileLoader();
+//      DataSourceType protocol = fileName.startsWith("http:")
+//              ? DataSourceType.URL
+//              : DataSourceType.FILE;
+        loader.LoadFile(viewport, (fileObject == null ? fileName : fileObject), protocol, currentFileFormat);
+    }
+    else
+    {
+      Rectangle bounds = this.getBounds();
 
-        AlignFrame newframe = null;
+      FileLoader loader = new FileLoader();
 
-        if (fileObject == null)
-        {
+      AlignFrame newframe = null;
 
-          DataSourceType protocol = (fileName.startsWith("http:")
-                  ? DataSourceType.URL
-                  : DataSourceType.FILE);
-          newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
-                  currentFileFormat);
-        }
-        else
-        {
-          newframe = loader.LoadFileWaitTillLoaded(fileObject,
-                  DataSourceType.FILE, currentFileFormat);
-        }
+      if (fileObject == null)
+      {
+        newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
+                currentFileFormat);
+      }
+      else
+      {
+        newframe = loader.LoadFileWaitTillLoaded(fileObject,
+                DataSourceType.FILE, currentFileFormat);
+      }
 
-        newframe.setBounds(bounds);
-        if (featureSettings != null && featureSettings.isShowing())
+      newframe.setBounds(bounds);
+      if (featureSettings != null && featureSettings.isShowing())
+      {
+        final Rectangle fspos = featureSettings.frame.getBounds();
+        // TODO: need a 'show feature settings' function that takes bounds -
+        // need to refactor Desktop.addFrame
+        newframe.featureSettings_actionPerformed(null);
+        final FeatureSettings nfs = newframe.featureSettings;
+        SwingUtilities.invokeLater(new Runnable()
         {
-          final Rectangle fspos = featureSettings.frame.getBounds();
-          // TODO: need a 'show feature settings' function that takes bounds -
-          // need to refactor Desktop.addFrame
-          newframe.featureSettings_actionPerformed(null);
-          final FeatureSettings nfs = newframe.featureSettings;
-          SwingUtilities.invokeLater(new Runnable()
+
+          @Override
+          public void run()
           {
-            @Override
-            public void run()
-            {
-              nfs.frame.setBounds(fspos);
-            }
-          });
-          this.featureSettings.close();
-          this.featureSettings = null;
-        }
-        this.closeMenuItem_actionPerformed(true);
+            nfs.frame.setBounds(fspos);
+          }
+        });
+        this.featureSettings.close();
+        this.featureSettings = null;
       }
+      this.closeMenuItem_actionPerformed(true);
     }
+
   }
 
   @Override
   public void addFromText_actionPerformed(ActionEvent e)
   {
-    Desktop.instance
+    Desktop.getInstance()
             .inputTextboxMenuItem_actionPerformed(viewport.getAlignPanel());
   }
 
   @Override
   public void addFromURL_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
+    Desktop.getInstance().inputURLMenuItem_actionPerformed(viewport);
   }
 
   @Override
@@ -1416,6 +1486,7 @@ public class AlignFrame extends GAlignFrame
    * Saves the alignment to a file with a name chosen by the user, if necessary
    * warning if a file would be overwritten
    */
+
   @Override
   public void saveAs_actionPerformed()
   {
@@ -1439,7 +1510,7 @@ public class AlignFrame extends GAlignFrame
     // todo is this (2005) test now obsolete - value is never null?
     while (currentFileFormat == null)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.select_file_format_before_saving"),
               MessageManager.getString("label.file_format_not_specified"),
@@ -1470,6 +1541,7 @@ public class AlignFrame extends GAlignFrame
    *
    * @return true if last call to saveAlignment(file, format) was successful.
    */
+
   public boolean isSaveAlignmentSuccessful()
   {
 
@@ -1502,6 +1574,7 @@ public class AlignFrame extends GAlignFrame
    * @param file
    * @param format
    */
+
   public void saveAlignment(String file, FileFormatI format)
   {
     lastSaveSuccessful = true;
@@ -1529,6 +1602,7 @@ public class AlignFrame extends GAlignFrame
     AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
     Runnable cancelAction = new Runnable()
     {
+
       @Override
       public void run()
       {
@@ -1537,6 +1611,7 @@ public class AlignFrame extends GAlignFrame
     };
     Runnable outputAction = new Runnable()
     {
+
       @Override
       public void run()
       {
@@ -1611,6 +1686,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param fileFormatName
    */
+
   @Override
   protected void outputText_actionPerformed(String fileFormatName)
   {
@@ -1619,6 +1695,7 @@ public class AlignFrame extends GAlignFrame
     AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
     Runnable outputAction = new Runnable()
     {
+
       @Override
       public void run()
       {
@@ -1669,6 +1746,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void htmlMenuItem_actionPerformed(ActionEvent e)
   {
@@ -1683,6 +1761,8 @@ public class AlignFrame extends GAlignFrame
     bjs.exportHTML(null);
   }
 
+  // ??
+
   public void createImageMap(File file, String image)
   {
     alignPanel.makePNGImageMap(file, image);
@@ -1694,6 +1774,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param f
    */
+
   @Override
   public void createPNG(File f)
   {
@@ -1706,6 +1787,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param f
    */
+
   @Override
   public void createEPS(File f)
   {
@@ -1718,6 +1800,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param f
    */
+
   @Override
   public void createSVG(File f)
   {
@@ -1737,6 +1820,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void printMenuItem_actionPerformed(ActionEvent e)
   {
@@ -1770,6 +1854,7 @@ public class AlignFrame extends GAlignFrame
     chooser.setToolTipText(tooltip);
     chooser.setResponseHandler(0, new Runnable()
     {
+
       @Override
       public void run()
       {
@@ -1788,6 +1873,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param closeAllTabs
    */
+
   @Override
   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
   {
@@ -1842,6 +1928,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param panelToClose
    */
+
   public void closeView(AlignmentPanel panelToClose)
   {
     int index = tabbedPane.getSelectedIndex();
@@ -1865,6 +1952,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * DOCUMENT ME!
    */
+
   void updateEditMenuBar()
   {
 
@@ -1918,6 +2006,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @return alignment objects for all views
    */
+
   AlignmentI[] getViewAlignments()
   {
     if (alignPanels != null)
@@ -1943,6 +2032,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void undoMenuItem_actionPerformed(ActionEvent e)
   {
@@ -1970,8 +2060,8 @@ public class AlignFrame extends GAlignFrame
       // && viewport.getColumnSelection().getHiddenColumns() != null &&
       // viewport.getColumnSelection()
       // .getHiddenColumns().size() > 0);
-      originalSource.firePropertyChange("alignment", null,
-              originalSource.getAlignment().getSequences());
+      originalSource.notifyAlignment();
+
     }
   }
 
@@ -1981,6 +2071,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void redoMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2010,8 +2101,8 @@ public class AlignFrame extends GAlignFrame
       // && viewport.getColumnSelection().getHiddenColumns() != null &&
       // viewport.getColumnSelection()
       // .getHiddenColumns().size() > 0);
-      originalSource.firePropertyChange("alignment", null,
-              originalSource.getAlignment().getSequences());
+      originalSource.notifyAlignment();
+
     }
   }
 
@@ -2063,6 +2154,7 @@ public class AlignFrame extends GAlignFrame
    * @param up
    *          DOCUMENT ME!
    */
+
   public void moveSelectedSequences(boolean up)
   {
     SequenceGroup sg = viewport.getSelectionGroup();
@@ -2187,6 +2279,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void copy_actionPerformed()
   {
@@ -2209,16 +2302,17 @@ public class AlignFrame extends GAlignFrame
 
     StringSelection ss = new StringSelection(output);
 
+    Desktop d = Desktop.getInstance();
     try
     {
-      jalview.gui.Desktop.internalCopy = true;
+      d.internalCopy = true;
       // Its really worth setting the clipboard contents
       // to empty before setting the large StringSelection!!
       Toolkit.getDefaultToolkit().getSystemClipboard()
               .setContents(new StringSelection(""), null);
 
       Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,
-              Desktop.instance);
+              Desktop.getInstance());
     } catch (OutOfMemoryError er)
     {
       new OOMWarning("copying region", er);
@@ -2238,7 +2332,7 @@ public class AlignFrame extends GAlignFrame
               hiddenCutoff, hiddenOffset);
     }
 
-    Desktop.jalviewClipboard = new Object[] { seqs,
+    d.jalviewClipboard = new Object[] { seqs,
         viewport.getAlignment().getDataset(), hiddenColumns };
     setStatus(MessageManager.formatMessage(
             "label.copied_sequences_to_clipboard", new Object[]
@@ -2253,6 +2347,7 @@ public class AlignFrame extends GAlignFrame
    * @throws InterruptedException
    * @throws IOException
    */
+
   @Override
   protected void pasteNew_actionPerformed(ActionEvent e)
           throws IOException, InterruptedException
@@ -2268,6 +2363,7 @@ public class AlignFrame extends GAlignFrame
    * @throws InterruptedException
    * @throws IOException
    */
+
   @Override
   protected void pasteThis_actionPerformed(ActionEvent e)
           throws IOException, InterruptedException
@@ -2318,12 +2414,14 @@ public class AlignFrame extends GAlignFrame
       boolean annotationAdded = false;
       AlignmentI alignment = null;
 
-      if (Desktop.jalviewClipboard != null)
+      Desktop d = Desktop.getInstance();
+
+      if (d.jalviewClipboard != null)
       {
         // The clipboard was filled from within Jalview, we must use the
         // sequences
         // And dataset from the copied alignment
-        SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
+        SequenceI[] newseq = (SequenceI[]) d.jalviewClipboard[0];
         // be doubly sure that we create *new* sequence objects.
         sequences = new SequenceI[newseq.length];
         for (int i = 0; i < newseq.length; i++)
@@ -2348,10 +2446,10 @@ public class AlignFrame extends GAlignFrame
       if (newAlignment)
       {
 
-        if (Desktop.jalviewClipboard != null)
+        if (d.jalviewClipboard != null)
         {
           // dataset is inherited
-          alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
+          alignment.setDataset((Alignment) d.jalviewClipboard[1]);
         }
         else
         {
@@ -2367,8 +2465,8 @@ public class AlignFrame extends GAlignFrame
         alignment = viewport.getAlignment();
         alwidth = alignment.getWidth() + 1;
         // decide if we need to import sequences from an existing dataset
-        boolean importDs = Desktop.jalviewClipboard != null
-                && Desktop.jalviewClipboard[1] != alignment.getDataset();
+        boolean importDs = d.jalviewClipboard != null
+                && d.jalviewClipboard[1] != alignment.getDataset();
         // importDs==true instructs us to copy over new dataset sequences from
         // an existing alignment
         Vector<SequenceI> newDs = (importDs) ? new Vector<>() : null; // used to
@@ -2549,8 +2647,7 @@ public class AlignFrame extends GAlignFrame
           }
           buildSortByAnnotationScoresMenu();
         }
-        viewport.firePropertyChange("alignment", null,
-                alignment.getSequences());
+        viewport.notifyAlignment();
         if (alignPanels != null)
         {
           for (AlignmentPanel ap : alignPanels)
@@ -2570,10 +2667,9 @@ public class AlignFrame extends GAlignFrame
                 DEFAULT_HEIGHT);
         String newtitle = new String("Copied sequences");
 
-        if (Desktop.jalviewClipboard != null
-                && Desktop.jalviewClipboard[2] != null)
+        if (d.jalviewClipboard != null && d.jalviewClipboard[2] != null)
         {
-          HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+          HiddenColumns hc = (HiddenColumns) d.jalviewClipboard[2];
           af.viewport.setHiddenColumns(hc);
         }
 
@@ -2624,11 +2720,10 @@ public class AlignFrame extends GAlignFrame
       AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
               DEFAULT_HEIGHT);
       String newtitle = new String("Flanking alignment");
-
-      if (Desktop.jalviewClipboard != null
-              && Desktop.jalviewClipboard[2] != null)
+      Desktop d = Desktop.getInstance();
+      if (d.jalviewClipboard != null && d.jalviewClipboard[2] != null)
       {
-        HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
+        HiddenColumns hc = (HiddenColumns) d.jalviewClipboard[2];
         af.viewport.setHiddenColumns(hc);
       }
 
@@ -2667,6 +2762,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Action Cut (delete and copy) the selected region
    */
+
   @Override
   protected void cut_actionPerformed()
   {
@@ -2677,6 +2773,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Performs menu option to Delete the currently selected region
    */
+
   @Override
   protected void delete_actionPerformed()
   {
@@ -2689,6 +2786,7 @@ public class AlignFrame extends GAlignFrame
 
     Runnable okAction = new Runnable()
     {
+
       @Override
       public void run()
       {
@@ -2705,8 +2803,8 @@ public class AlignFrame extends GAlignFrame
         viewport.sendSelection();
         viewport.getAlignment().deleteGroup(sg);
 
-        viewport.firePropertyChange("alignment", null,
-                viewport.getAlignment().getSequences());
+        viewport.notifyAlignment();
+
         if (viewport.getAlignment().getHeight() < 1)
         {
           try
@@ -2715,6 +2813,8 @@ public class AlignFrame extends GAlignFrame
           } catch (Exception ex)
           {
           }
+        } else {
+          updateAll(null);
         }
       }
     };
@@ -2728,7 +2828,8 @@ public class AlignFrame extends GAlignFrame
             + 1) == viewport.getAlignment().getWidth()) ? true : false;
     if (wholeHeight && wholeWidth)
     {
-      JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+      JvOptionPane dialog = JvOptionPane
+              .newOptionDialog(Desktop.getDesktopPane());
       dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
       Object[] options = new Object[] {
           MessageManager.getString("action.ok"),
@@ -2750,15 +2851,32 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void deleteGroups_actionPerformed(ActionEvent e)
   {
     if (avc.deleteGroups())
     {
-      PaintRefresher.Refresh(this, viewport.getSequenceSetId());
-      alignPanel.updateAnnotation();
+      updateAll(viewport.getSequenceSetId());
+    }
+  }
+
+  private void updateAll(String id)
+  {
+    if (id == null)
+    {
+      // this will force a non-fast repaint of both the IdPanel and SeqPanel
+      alignPanel.getIdPanel().getIdCanvas().setNoFastPaint();
+      alignPanel.getSeqPanel().seqCanvas.setNoFastPaint();
+      alignPanel.repaint();
+    }
+    else
+    {
+      // original version
+      PaintRefresher.Refresh(this, id);
       alignPanel.paintAlignment(true, true);
     }
+    alignPanel.updateAnnotation();
   }
 
   /**
@@ -2767,21 +2885,11 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
   {
-    SequenceGroup sg = new SequenceGroup(
-            viewport.getAlignment().getSequences());
-
-    sg.setEndRes(viewport.getAlignment().getWidth() - 1);
-    viewport.setSelectionGroup(sg);
-    viewport.isSelectionGroupChanged(true);
-    viewport.sendSelection();
-    // JAL-2034 - should delegate to
-    // alignPanel to decide if overview needs
-    // updating.
-    alignPanel.paintAlignment(false, false);
-    PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
+    alignPanel.selectAllSequences();
   }
 
   /**
@@ -2790,24 +2898,11 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
   {
-    if (viewport.cursorMode)
-    {
-      alignPanel.getSeqPanel().keyboardNo1 = null;
-      alignPanel.getSeqPanel().keyboardNo2 = null;
-    }
-    viewport.setSelectionGroup(null);
-    viewport.getColumnSelection().clear();
-    viewport.setSelectionGroup(null);
-    alignPanel.getIdPanel().getIdCanvas().searchResults = null;
-    // JAL-2034 - should delegate to
-    // alignPanel to decide if overview needs
-    // updating.
-    alignPanel.paintAlignment(false, false);
-    PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
-    viewport.sendSelection();
+    alignPanel.deselectAllSequences();
   }
 
   /**
@@ -2816,6 +2911,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2823,7 +2919,7 @@ public class AlignFrame extends GAlignFrame
 
     if (sg == null)
     {
-      selectAllSequenceMenuItem_actionPerformed(null);
+      alignPanel.selectAllSequences();
 
       return;
     }
@@ -2855,6 +2951,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2867,6 +2964,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2913,8 +3011,8 @@ public class AlignFrame extends GAlignFrame
                 column, viewport.getAlignment());
       }
 
-      setStatus(MessageManager
-              .formatMessage("label.removed_columns", new String[]
+      setStatus(MessageManager.formatMessage("label.removed_columns",
+              new String[]
               { Integer.valueOf(trimRegion.getSize()).toString() }));
 
       addHistoryItem(trimRegion);
@@ -2928,8 +3026,8 @@ public class AlignFrame extends GAlignFrame
         }
       }
 
-      viewport.firePropertyChange("alignment", null,
-              viewport.getAlignment().getSequences());
+      viewport.notifyAlignment();
+
     }
   }
 
@@ -2939,6 +3037,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
   {
@@ -2963,8 +3062,8 @@ public class AlignFrame extends GAlignFrame
 
     addHistoryItem(removeGapCols);
 
-    setStatus(MessageManager
-            .formatMessage("label.removed_empty_columns", new Object[]
+    setStatus(MessageManager.formatMessage("label.removed_empty_columns",
+            new Object[]
             { Integer.valueOf(removeGapCols.getSize()).toString() }));
 
     // This is to maintain viewport position on first residue
@@ -2978,8 +3077,8 @@ public class AlignFrame extends GAlignFrame
     // if (viewport.hasHiddenColumns)
     // viewport.getColumnSelection().compensateForEdits(shifts);
     ranges.setStartRes(seq.findIndex(startRes) - 1);
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
+
 
   }
 
@@ -2989,6 +3088,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3016,9 +3116,7 @@ public class AlignFrame extends GAlignFrame
             viewport.getAlignment()));
 
     viewport.getRanges().setStartRes(seq.findIndex(startRes) - 1);
-
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
 
   }
 
@@ -3028,12 +3126,13 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void padGapsMenuitem_actionPerformed(ActionEvent e)
   {
     viewport.setPadGaps(padGapsMenuitem.isSelected());
-    viewport.firePropertyChange("alignment", null,
-            viewport.getAlignment().getSequences());
+    viewport.notifyAlignment();
+
   }
 
   /**
@@ -3042,6 +3141,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void findMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3051,6 +3151,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Create a new view of the current alignment.
    */
+
   @Override
   public void newView_actionPerformed(ActionEvent e)
   {
@@ -3066,6 +3167,7 @@ public class AlignFrame extends GAlignFrame
    *          if true then duplicate all annnotation, groups and settings
    * @return new alignment panel, already displayed.
    */
+
   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
   {
     /*
@@ -3086,8 +3188,8 @@ public class AlignFrame extends GAlignFrame
 
     if (viewport.getViewName() == null)
     {
-      viewport.setViewName(MessageManager
-              .getString("label.view_name_original"));
+      viewport.setViewName(
+              MessageManager.getString("label.view_name_original"));
     }
 
     /*
@@ -3138,6 +3240,7 @@ public class AlignFrame extends GAlignFrame
    * @param viewTitle
    * @return
    */
+
   protected String getNewViewName(String viewTitle)
   {
     int index = Desktop.getViewCount(viewport.getSequenceSetId());
@@ -3172,6 +3275,7 @@ public class AlignFrame extends GAlignFrame
    * @param comps
    * @return
    */
+
   protected List<String> getExistingViewNames(List<Component> comps)
   {
     List<String> existingNames = new ArrayList<>();
@@ -3192,6 +3296,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Explode tabbed views into separate windows.
    */
+
   @Override
   public void expandViews_actionPerformed(ActionEvent e)
   {
@@ -3201,10 +3306,11 @@ public class AlignFrame extends GAlignFrame
   /**
    * Gather views in separate windows back into a tabbed presentation.
    */
+
   @Override
   public void gatherViews_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.gatherViews(this);
+    Desktop.getInstance().gatherViews(this);
   }
 
   /**
@@ -3213,6 +3319,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void font_actionPerformed(ActionEvent e)
   {
@@ -3225,6 +3332,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void seqLimit_actionPerformed(ActionEvent e)
   {
@@ -3254,6 +3362,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
    */
+
   @Override
   protected void followHighlight_actionPerformed()
   {
@@ -3275,6 +3384,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3288,6 +3398,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void wrapMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3324,6 +3435,7 @@ public class AlignFrame extends GAlignFrame
    * @param toggleSeqs
    * @param toggleCols
    */
+
   protected void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
   {
 
@@ -3391,6 +3503,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
    * event.ActionEvent)
    */
+
   @Override
   public void hideAllButSelection_actionPerformed(ActionEvent e)
   {
@@ -3405,6 +3518,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
    * .ActionEvent)
    */
+
   @Override
   public void hideAllSelection_actionPerformed(ActionEvent e)
   {
@@ -3424,6 +3538,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
    * ActionEvent)
    */
+
   @Override
   public void showAllhidden_actionPerformed(ActionEvent e)
   {
@@ -3455,6 +3570,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void scaleAbove_actionPerformed(ActionEvent e)
   {
@@ -3469,6 +3585,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void scaleLeft_actionPerformed(ActionEvent e)
   {
@@ -3483,6 +3600,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void scaleRight_actionPerformed(ActionEvent e)
   {
@@ -3497,6 +3615,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3510,6 +3629,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void viewTextMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3523,6 +3643,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3568,6 +3689,7 @@ public class AlignFrame extends GAlignFrame
    * @param evt
    *          DOCUMENT ME!
    */
+
   @Override
   public void showSeqFeatures_actionPerformed(ActionEvent evt)
   {
@@ -3584,16 +3706,32 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param e
    */
+
   @Override
   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
   {
     final boolean setVisible = annotationPanelMenuItem.isSelected();
     viewport.setShowAnnotation(setVisible);
-    this.showAllSeqAnnotations.setEnabled(setVisible);
-    this.hideAllSeqAnnotations.setEnabled(setVisible);
-    this.showAllAlAnnotations.setEnabled(setVisible);
-    this.hideAllAlAnnotations.setEnabled(setVisible);
+    syncAnnotationMenuItems(setVisible);
     alignPanel.updateLayout();
+    repaint();
+    SwingUtilities.invokeLater(new Runnable() {
+
+      @Override
+      public void run()
+      {
+        alignPanel.updateScrollBarsFromRanges();
+      }
+      
+    });
+  }
+
+  private void syncAnnotationMenuItems(boolean setVisible)
+  {
+    showAllSeqAnnotations.setEnabled(setVisible);
+    hideAllSeqAnnotations.setEnabled(setVisible);
+    showAllAlAnnotations.setEnabled(setVisible);
+    hideAllAlAnnotations.setEnabled(setVisible);
   }
 
   @Override
@@ -3647,6 +3785,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void overviewMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3656,17 +3795,42 @@ public class AlignFrame extends GAlignFrame
     }
 
     JInternalFrame frame = new JInternalFrame();
-    final OverviewPanel overview = new OverviewPanel(alignPanel);
+
+    // BH 2019.07.26 we allow for an embedded
+    // undecorated overview with defined size
+    frame.setName(Platform.getAppID("overview"));
+    //
+    Dimension dim = Platform.getDimIfEmbedded(frame, -1, -1);
+    if (dim != null && dim.width == 0)
+    {
+      dim = null; // hidden, not embedded
+    }
+    OverviewPanel overview = new OverviewPanel(alignPanel, dim);
+
     frame.setContentPane(overview);
+    if (dim == null)
+    {
+      dim = new Dimension();
+      // was frame.getSize(), but that is 0,0 at this point;
+    }
+    else
+    {
+      // we are imbedding, and so we have an undecorated frame
+      // and we can set the the frame dimensions accordingly.
+    }
+    // allowing for unresizable option using, style="resize:none"
+    boolean resizable = (Platform.getEmbeddedAttribute(frame,
+            "resize") != "none");
     Desktop.addInternalFrame(frame, MessageManager
             .formatMessage("label.overview_params", new Object[]
-            { this.getTitle() }), true, frame.getWidth(), frame.getHeight(),
-            true, true);
+            { this.getTitle() }), Desktop.FRAME_MAKE_VISIBLE, dim.width,
+            dim.height, resizable, Desktop.FRAME_ALLOW_ANY_SIZE);
     frame.pack();
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     frame.addInternalFrameListener(
             new javax.swing.event.InternalFrameAdapter()
             {
+
               @Override
               public void internalFrameClosed(
                       javax.swing.event.InternalFrameEvent evt)
@@ -3695,6 +3859,7 @@ public class AlignFrame extends GAlignFrame
    * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
    * ()[0])); }
    */
+
   @Override
   public void annotationColour_actionPerformed()
   {
@@ -3714,6 +3879,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param selected
    */
+
   @Override
   public void applyToAllGroups_actionPerformed(boolean selected)
   {
@@ -3726,6 +3892,7 @@ public class AlignFrame extends GAlignFrame
    * @param name
    *          the name (not the menu item label!) of the colour scheme
    */
+
   @Override
   public void changeColour_actionPerformed(String name)
   {
@@ -3753,6 +3920,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param cs
    */
+
   @Override
   public void changeColour(ColourSchemeI cs)
   {
@@ -3767,6 +3935,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Show the PID threshold slider panel
    */
+
   @Override
   protected void modifyPID_actionPerformed()
   {
@@ -3778,6 +3947,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Show the Conservation slider panel
    */
+
   @Override
   protected void modifyConservation_actionPerformed()
   {
@@ -3789,6 +3959,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Action on selecting or deselecting (Colour) By Conservation
    */
+
   @Override
   public void conservationMenuItem_actionPerformed(boolean selected)
   {
@@ -3810,6 +3981,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Action on selecting or deselecting (Colour) Above PID Threshold
    */
+
   @Override
   public void abovePIDThreshold_actionPerformed(boolean selected)
   {
@@ -3838,6 +4010,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3855,6 +4028,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void sortIDMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3871,6 +4045,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3887,6 +4062,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3926,6 +4102,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3938,6 +4115,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
   {
@@ -3963,11 +4141,14 @@ public class AlignFrame extends GAlignFrame
   @Override
   public void autoCalculate_actionPerformed(ActionEvent e)
   {
-    viewport.autoCalculateConsensus = autoCalculate.isSelected();
-    if (viewport.autoCalculateConsensus)
+    viewport.setAutoCalculateConsensusAndConservation(
+            autoCalculate.isSelected());
+    if (viewport.getAutoCalculateConsensusAndConservation())
+    // ??
+    // viewport.autoCalculateConsensus = autoCalculate.isSelected();
+    // if (viewport.autoCalculateConsensus)
     {
-      viewport.firePropertyChange("alignment", null,
-              viewport.getAlignment().getSequences());
+      viewport.notifyAlignment();
     }
   }
 
@@ -3993,6 +4174,7 @@ public class AlignFrame extends GAlignFrame
    * @param options
    *          parameters for the distance or similarity calculation
    */
+
   void newTreePanel(String type, String modelName,
           SimilarityParamsI options)
   {
@@ -4010,7 +4192,7 @@ public class AlignFrame extends GAlignFrame
       {
         if (_s.getLength() < sg.getEndRes())
         {
-          JvOptionPane.showMessageDialog(Desktop.desktop,
+          JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
                   MessageManager.getString(
                           "label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
                   MessageManager.getString(
@@ -4042,7 +4224,8 @@ public class AlignFrame extends GAlignFrame
 
     frameTitle += this.title;
 
-    Desktop.addInternalFrame(tp, frameTitle, 600, 500);
+    Dimension dim = Platform.getDimIfEmbedded(tp, 600, 500);
+    Desktop.addInternalFrame(tp, frameTitle, dim.width, dim.height);
   }
 
   /**
@@ -4053,6 +4236,7 @@ public class AlignFrame extends GAlignFrame
    * @param order
    *          DOCUMENT ME!
    */
+
   public void addSortByOrderMenuItem(String title,
           final AlignmentOrder order)
   {
@@ -4062,6 +4246,7 @@ public class AlignFrame extends GAlignFrame
     sort.add(item);
     item.addActionListener(new java.awt.event.ActionListener()
     {
+
       @Override
       public void actionPerformed(ActionEvent e)
       {
@@ -4088,6 +4273,7 @@ public class AlignFrame extends GAlignFrame
    *          the label used to retrieve scores for each sequence on the
    *          alignment
    */
+
   public void addSortByAnnotScoreMenuItem(JMenu sort,
           final String scoreLabel)
   {
@@ -4095,6 +4281,7 @@ public class AlignFrame extends GAlignFrame
     sort.add(item);
     item.addActionListener(new java.awt.event.ActionListener()
     {
+
       @Override
       public void actionPerformed(ActionEvent e)
       {
@@ -4120,6 +4307,7 @@ public class AlignFrame extends GAlignFrame
    * rebuilding in subsequence calls.
    * 
    */
+
   @Override
   public void buildSortByAnnotationScoresMenu()
   {
@@ -4159,41 +4347,38 @@ public class AlignFrame extends GAlignFrame
   }
 
   /**
+   * Enable (or, if desired, make visible) the By Tree 
+   * submenu only if it has at least one element (or will have).
+   * 
+   */
+  @Override
+  protected void enableSortMenuOptions()
+  {
+    List<TreePanel> treePanels = getTreePanels();
+    sortByTreeMenu.setEnabled(!treePanels.isEmpty());
+  }
+  
+  /**
    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
    * call. Listeners are added to remove the menu item when the treePanel is
    * closed, and adjust the tree leaf to sequence mapping when the alignment is
    * modified.
    */
+
   @Override
   public void buildTreeSortMenu()
   {
     sortByTreeMenu.removeAll();
 
-    List<Component> comps = PaintRefresher.components
-            .get(viewport.getSequenceSetId());
-    List<TreePanel> treePanels = new ArrayList<>();
-    for (Component comp : comps)
-    {
-      if (comp instanceof TreePanel)
-      {
-        treePanels.add((TreePanel) comp);
-      }
-    }
-
-    if (treePanels.size() < 1)
-    {
-      sortByTreeMenu.setVisible(false);
-      return;
-    }
-
-    sortByTreeMenu.setVisible(true);
+    List<TreePanel> treePanels = getTreePanels();
 
     for (final TreePanel tp : treePanels)
     {
       final JMenuItem item = new JMenuItem(tp.getTitle());
       item.addActionListener(new java.awt.event.ActionListener()
       {
+
         @Override
         public void actionPerformed(ActionEvent e)
         {
@@ -4207,6 +4392,21 @@ public class AlignFrame extends GAlignFrame
     }
   }
 
+  private List<TreePanel> getTreePanels()
+  {
+    List<Component> comps = PaintRefresher.components
+            .get(viewport.getSequenceSetId());
+    List<TreePanel> treePanels = new ArrayList<>();
+    for (Component comp : comps)
+    {
+      if (comp instanceof TreePanel)
+      {
+        treePanels.add((TreePanel) comp);
+      }
+    }
+    return treePanels;
+  }
+
   public boolean sortBy(AlignmentOrder alorder, String undoname)
   {
     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
@@ -4225,6 +4425,7 @@ public class AlignFrame extends GAlignFrame
    * be submitted for multiple alignment.
    * 
    */
+
   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
   {
     // Now, check we have enough sequences
@@ -4270,6 +4471,7 @@ public class AlignFrame extends GAlignFrame
    * region or the whole alignment. (where the first sequence in the set is the
    * one that the prediction will be for).
    */
+
   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
   {
     AlignmentView seqs = null;
@@ -4303,6 +4505,7 @@ public class AlignFrame extends GAlignFrame
    * @param e
    *          DOCUMENT ME!
    */
+
   @Override
   protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
   {
@@ -4317,6 +4520,7 @@ public class AlignFrame extends GAlignFrame
 
     chooser.setResponseHandler(0, new Runnable()
     {
+
       @Override
       public void run()
       {
@@ -4330,7 +4534,8 @@ public class AlignFrame extends GAlignFrame
           viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
         } catch (Exception ex)
         {
-          JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
+          JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
+                  ex.getMessage(),
                   MessageManager
                           .getString("label.problem_reading_tree_file"),
                   JvOptionPane.WARNING_MESSAGE);
@@ -4338,7 +4543,7 @@ public class AlignFrame extends GAlignFrame
         }
         if (fin != null && fin.hasWarningMessage())
         {
-          JvOptionPane.showMessageDialog(Desktop.desktop,
+          JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
                   fin.getWarningMessage(),
                   MessageManager.getString(
                           "label.possible_problem_with_tree_file"),
@@ -4380,6 +4585,7 @@ public class AlignFrame extends GAlignFrame
    *          position
    * @return TreePanel handle
    */
+
   public TreePanel showNewickTree(NewickFile nf, String treeTitle,
           AlignmentView input, int w, int h, int x, int y)
   {
@@ -4392,15 +4598,24 @@ public class AlignFrame extends GAlignFrame
       if (nf.getTree() != null)
       {
         tp = new TreePanel(alignPanel, nf, treeTitle, input);
-
-        tp.setSize(w, h);
+        Dimension dim = Platform.getDimIfEmbedded(tp, -1, -1);
+        if (dim == null)
+        {
+          dim = new Dimension(w, h);
+        }
+        else
+        {
+          // no offset, either
+          x = 0;
+        }
+        tp.setSize(dim.width, dim.height);
 
         if (x > 0 && y > 0)
         {
           tp.setLocation(x, y);
         }
 
-        Desktop.addInternalFrame(tp, treeTitle, w, h);
+        Desktop.addInternalFrame(tp, treeTitle, dim.width, dim.height);
       }
     } catch (Exception ex)
     {
@@ -4428,7 +4643,7 @@ public class AlignFrame extends GAlignFrame
       }
       if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
       {
-        WSDiscovererI jws2servs = Jws2Discoverer.getDiscoverer();
+        WSDiscovererI jws2servs = Jws2Discoverer.getInstance();
         JMenu submenu = new JMenu("JABAWS");
         buildLegacyWebServicesMenu(submenu);
         buildWebServicesMenu(jws2servs, submenu);
@@ -4440,9 +4655,9 @@ public class AlignFrame extends GAlignFrame
   private void buildLegacyWebServicesMenu(JMenu menu)
   {
     JMenu secstrmenu = new JMenu("Secondary Structure Prediction");
-    if (Discoverer.services != null && Discoverer.services.size() > 0) 
+    if (Discoverer.getServices() != null && Discoverer.getServices().size() > 0) 
     {
-      var secstrpred = Discoverer.services.get("SecStrPred");
+      var secstrpred = Discoverer.getServices().get("SecStrPred");
       if (secstrpred != null) 
       {
         for (ext.vamsas.ServiceHandle sh : secstrpred) 
@@ -4491,6 +4706,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param webService
    */
+
   protected void build_urlServiceMenu(JMenu webService)
   {
     // TODO: remove this code when 2.7 is released
@@ -4499,7 +4715,7 @@ public class AlignFrame extends GAlignFrame
      * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final
      * AlignFrame af = this; testAlView.addActionListener(new ActionListener() {
      * 
-     * @Override public void actionPerformed(ActionEvent e) {
+     *  public void actionPerformed(ActionEvent e) {
      * jalview.datamodel.AlignmentView
      * .testSelectionViews(af.viewport.getAlignment(),
      * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
@@ -4528,6 +4744,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @return true if Show Cross-references menu should be enabled
    */
+
   public boolean canShowProducts()
   {
     SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
@@ -4555,6 +4772,7 @@ public class AlignFrame extends GAlignFrame
         JMenuItem xtype = new JMenuItem(source);
         xtype.addActionListener(new ActionListener()
         {
+
           @Override
           public void actionPerformed(ActionEvent e)
           {
@@ -4587,6 +4805,7 @@ public class AlignFrame extends GAlignFrame
    * @param source
    *          the database to show cross-references for
    */
+
   protected void showProductsFor(final SequenceI[] sel, final boolean _odna,
           final String source)
   {
@@ -4598,6 +4817,7 @@ public class AlignFrame extends GAlignFrame
    * Construct and display a new frame containing the translation of this
    * frame's DNA sequences to their aligned protein (amino acid) equivalents.
    */
+
   @Override
   public void showTranslation_actionPerformed(GeneticCodeI codeTable)
   {
@@ -4616,8 +4836,8 @@ public class AlignFrame extends GAlignFrame
       final String errorTitle = MessageManager
               .getString("label.implementation_error")
               + MessageManager.getString("label.translation_failed");
-      JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
-              JvOptionPane.ERROR_MESSAGE);
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg,
+              errorTitle, JvOptionPane.ERROR_MESSAGE);
       return;
     }
     if (al == null || al.getHeight() == 0)
@@ -4626,8 +4846,8 @@ public class AlignFrame extends GAlignFrame
               "label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
       final String errorTitle = MessageManager
               .getString("label.translation_failed");
-      JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
-              JvOptionPane.WARNING_MESSAGE);
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), msg,
+              errorTitle, JvOptionPane.WARNING_MESSAGE);
     }
     else
     {
@@ -4640,7 +4860,7 @@ public class AlignFrame extends GAlignFrame
       if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
       {
         final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
-        viewport.openSplitFrame(af, new Alignment(seqs));
+        AlignViewport.openSplitFrame(this, af, new Alignment(seqs));
       }
       else
       {
@@ -4655,6 +4875,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param format
    */
+
   public void setFileFormat(FileFormatI format)
   {
     this.currentFileFormat = format;
@@ -4669,11 +4890,12 @@ public class AlignFrame extends GAlignFrame
    *          access mode of file (see jalview.io.AlignFile)
    * @return true if features file was parsed correctly.
    */
+
   public boolean parseFeaturesFile(Object file, DataSourceType sourceType)
   {
     // BH 2018
     return avc.parseFeaturesFile(file, sourceType,
-            Cache.getDefault("RELAXEDSEQIDMATCHING", false));
+            Cache.getDefault(Preferences.RELAXEDSEQIDMATCHING, false));
 
   }
 
@@ -4719,187 +4941,190 @@ public class AlignFrame extends GAlignFrame
     evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
 
-    final AlignFrame thisaf = this;
     final List<Object> files = new ArrayList<>();
     List<DataSourceType> protocols = new ArrayList<>();
 
     try
     {
       Desktop.transferFromDropTarget(files, protocols, evt, t);
+      if (files.size() > 0)
+      {
+        new Thread(new Runnable()
+        {
+
+          @Override
+          public void run()
+          {
+            loadDroppedFiles(files, protocols, evt, t);
+          }
+        }).start();
+      }
     } catch (Exception e)
     {
       e.printStackTrace();
     }
-    if (files != null)
+  }
+
+  protected void loadDroppedFiles(List<Object> files,
+          List<DataSourceType> protocols, DropTargetDropEvent evt,
+          Transferable t)
+  {
+    try
     {
-      new Thread(new Runnable()
+      // check to see if any of these files have names matching sequences
+      // in
+      // the alignment
+      SequenceIdMatcher idm = new SequenceIdMatcher(
+              viewport.getAlignment().getSequencesArray());
+      /**
+       * Object[] { String,SequenceI}
+       */
+      ArrayList<Object[]> filesmatched = new ArrayList<>();
+      ArrayList<Object> filesnotmatched = new ArrayList<>();
+      for (int i = 0; i < files.size(); i++)
       {
-        @Override
-        public void run()
+        // BH 2018
+        Object fileObj = files.get(i);
+        String fileName = fileObj.toString();
+        String pdbfn = "";
+        DataSourceType protocol = (fileObj instanceof File
+                ? DataSourceType.FILE
+                : FormatAdapter.checkProtocol(fileName));
+        if (protocol == DataSourceType.FILE)
         {
-          try
+          File file;
+          if (fileObj instanceof File)
+          {
+            file = (File) fileObj;
+            Platform.cacheFileData(file);
+          }
+          else
           {
-            // check to see if any of these files have names matching sequences
-            // in
-            // the alignment
-            SequenceIdMatcher idm = new SequenceIdMatcher(
-                    viewport.getAlignment().getSequencesArray());
-            /**
-             * Object[] { String,SequenceI}
-             */
-            ArrayList<Object[]> filesmatched = new ArrayList<>();
-            ArrayList<Object> filesnotmatched = new ArrayList<>();
-            for (int i = 0; i < files.size(); i++)
+            file = new File(fileName);
+          }
+          pdbfn = file.getName();
+        }
+        else if (protocol == DataSourceType.URL)
+        {
+          URL url = new URL(fileName);
+          pdbfn = url.getFile();
+        }
+        if (pdbfn.length() > 0)
+        {
+          // attempt to find a match in the alignment
+          SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
+          int l = 0, c = pdbfn.indexOf(".");
+          while (mtch == null && c != -1)
+          {
+            do
             {
-              // BH 2018
-              Object file = files.get(i);
-              String fileName = file.toString();
-              String pdbfn = "";
-              DataSourceType protocol = (file instanceof File
-                      ? DataSourceType.FILE
-                      : FormatAdapter.checkProtocol(fileName));
-              if (protocol == DataSourceType.FILE)
-              {
-                File fl;
-                if (file instanceof File)
-                {
-                  fl = (File) file;
-                  Platform.cacheFileData(fl);
-                }
-                else
-                {
-                  fl = new File(fileName);
-                }
-                pdbfn = fl.getName();
-              }
-              else if (protocol == DataSourceType.URL)
-              {
-                URL url = new URL(fileName);
-                pdbfn = url.getFile();
-              }
-              if (pdbfn.length() > 0)
-              {
-                // attempt to find a match in the alignment
-                SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
-                int l = 0, c = pdbfn.indexOf(".");
-                while (mtch == null && c != -1)
-                {
-                  do
-                  {
-                    l = c;
-                  } while ((c = pdbfn.indexOf(".", l)) > l);
-                  if (l > -1)
-                  {
-                    pdbfn = pdbfn.substring(0, l);
-                  }
-                  mtch = idm.findAllIdMatches(pdbfn);
-                }
-                if (mtch != null)
-                {
-                  FileFormatI type;
-                  try
-                  {
-                    type = new IdentifyFile().identify(file, protocol);
-                  } catch (Exception ex)
-                  {
-                    type = null;
-                  }
-                  if (type != null && type.isStructureFile())
-                  {
-                    filesmatched.add(new Object[] { file, protocol, mtch });
-                    continue;
-                  }
-                }
-                // File wasn't named like one of the sequences or wasn't a PDB
-                // file.
-                filesnotmatched.add(file);
-              }
+              l = c;
+            } while ((c = pdbfn.indexOf(".", l)) > l);
+            if (l > -1)
+            {
+              pdbfn = pdbfn.substring(0, l);
             }
-            int assocfiles = 0;
-            if (filesmatched.size() > 0)
+            mtch = idm.findAllIdMatches(pdbfn);
+          }
+          if (mtch != null)
+          {
+            FileFormatI type;
+            try
             {
-              boolean autoAssociate = Cache
-                      .getDefault("AUTOASSOCIATE_PDBANDSEQS", false);
-              if (!autoAssociate)
-              {
-                String msg = MessageManager.formatMessage(
-                        "label.automatically_associate_structure_files_with_sequences_same_name",
-                        new Object[]
-                        { Integer.valueOf(filesmatched.size())
-                                .toString() });
-                String ttl = MessageManager.getString(
-                        "label.automatically_associate_structure_files_by_name");
-                int choice = JvOptionPane.showConfirmDialog(thisaf, msg,
-                        ttl, JvOptionPane.YES_NO_OPTION);
-                autoAssociate = choice == JvOptionPane.YES_OPTION;
-              }
-              if (autoAssociate)
-              {
-                for (Object[] fm : filesmatched)
-                {
-                  // try and associate
-                  // TODO: may want to set a standard ID naming formalism for
-                  // associating PDB files which have no IDs.
-                  for (SequenceI toassoc : (SequenceI[]) fm[2])
-                  {
-                    PDBEntry pe = new AssociatePdbFileWithSeq()
-                            .associatePdbWithSeq(fm[0].toString(),
-                                    (DataSourceType) fm[1], toassoc, false,
-                                    Desktop.instance);
-                    if (pe != null)
-                    {
-                      System.err.println("Associated file : "
-                              + (fm[0].toString()) + " with "
-                              + toassoc.getDisplayId(true));
-                      assocfiles++;
-                    }
-                  }
-                  // TODO: do we need to update overview ? only if features are
-                  // shown I guess
-                  alignPanel.paintAlignment(true, false);
-                }
-              }
-              else
-              {
-                /*
-                 * add declined structures as sequences
-                 */
-                for (Object[] o : filesmatched)
-                {
-                  filesnotmatched.add(o[0]);
-                }
-              }
+              type = new IdentifyFile().identify(fileObj, protocol);
+            } catch (Exception ex)
+            {
+              type = null;
             }
-            if (filesnotmatched.size() > 0)
+            if (type != null && type.isStructureFile())
             {
-              if (assocfiles > 0 && (Cache.getDefault(
-                      "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
-                      || JvOptionPane.showConfirmDialog(thisaf,
-                              "<html>" + MessageManager.formatMessage(
-                                      "label.ignore_unmatched_dropped_files_info",
-                                      new Object[]
-                                      { Integer.valueOf(
-                                              filesnotmatched.size())
-                                              .toString() })
-                                      + "</html>",
-                              MessageManager.getString(
-                                      "label.ignore_unmatched_dropped_files"),
-                              JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
-              {
-                return;
-              }
-              for (Object fn : filesnotmatched)
+              filesmatched.add(new Object[] { fileObj, protocol, mtch });
+              continue;
+            }
+          }
+          // File wasn't named like one of the sequences or wasn't a PDB
+          // file.
+          filesnotmatched.add(fileObj);
+        }
+      }
+      int assocfiles = 0;
+      if (filesmatched.size() > 0)
+      {
+        boolean autoAssociate = Cache
+                .getDefault(Preferences.AUTOASSOCIATE_PDBANDSEQS, false);
+        if (!autoAssociate)
+        {
+          String msg = MessageManager.formatMessage(
+                  "label.automatically_associate_structure_files_with_sequences_same_name",
+                  new Object[]
+                  { Integer.valueOf(filesmatched.size()).toString() });
+          String ttl = MessageManager.getString(
+                  "label.automatically_associate_structure_files_by_name");
+          int choice = JvOptionPane.showConfirmDialog(this, msg, ttl,
+                  JvOptionPane.YES_NO_OPTION);
+          autoAssociate = choice == JvOptionPane.YES_OPTION;
+        }
+        if (autoAssociate)
+        {
+          for (Object[] fm : filesmatched)
+          {
+            // try and associate
+            // TODO: may want to set a standard ID naming formalism for
+            // associating PDB files which have no IDs.
+            for (SequenceI toassoc : (SequenceI[]) fm[2])
+            {
+              PDBEntry pe = AssociatePdbFileWithSeq.associatePdbWithSeq(
+                      fm[0].toString(), (DataSourceType) fm[1], toassoc,
+                      false);
+              if (pe != null)
               {
-                loadJalviewDataFile(fn, null, null, null);
+                System.err.println("Associated file : " + (fm[0].toString())
+                        + " with " + toassoc.getDisplayId(true));
+                assocfiles++;
               }
-
             }
-          } catch (Exception ex)
+            // TODO: do we need to update overview ? only if features are
+            // shown I guess
+            alignPanel.paintAlignment(true, false);
+          }
+        }
+        else
+        {
+          /*
+           * add declined structures as sequences
+           */
+          for (Object[] o : filesmatched)
           {
-            ex.printStackTrace();
+            filesnotmatched.add(o[0]);
           }
         }
-      }).start();
+      }
+      if (filesnotmatched.size() > 0)
+      {
+        if (assocfiles > 0 && (Cache
+                .getDefault("AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
+                || JvOptionPane.showConfirmDialog(this,
+                        "<html>" + MessageManager.formatMessage(
+                                "label.ignore_unmatched_dropped_files_info",
+                                new Object[]
+                                { Integer.valueOf(filesnotmatched.size())
+                                        .toString() })
+                                + "</html>",
+                        MessageManager.getString(
+                                "label.ignore_unmatched_dropped_files"),
+                        JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
+        {
+          return;
+        }
+        for (Object fn : filesnotmatched)
+        {
+          loadJalviewDataFile(fn, null, null, null);
+        }
+
+      }
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
     }
   }
 
@@ -4917,6 +5142,7 @@ public class AlignFrame extends GAlignFrame
    * @throws InterruptedException
    * @throws IOException
    */
+
   public void loadJalviewDataFile(Object file, DataSourceType sourceType,
           FileFormatI format, SequenceI assocSeq)
   {
@@ -4958,7 +5184,7 @@ public class AlignFrame extends GAlignFrame
             {
               // some problem - if no warning its probable that the ID matching
               // process didn't work
-              JvOptionPane.showMessageDialog(Desktop.desktop,
+              JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
                       tcf.getWarningMessage() == null
                               ? MessageManager.getString(
                                       "label.check_file_matches_sequence_ids_alignment")
@@ -5031,10 +5257,7 @@ public class AlignFrame extends GAlignFrame
       }
       if (isAnnotation)
       {
-        alignPanel.adjustAnnotationHeight();
-        viewport.updateSequenceIdColours();
-        buildSortByAnnotationScoresMenu();
-        alignPanel.paintAlignment(true, true);
+        updateForAnnotations();
       }
     } catch (Exception ex)
     {
@@ -5058,7 +5281,47 @@ public class AlignFrame extends GAlignFrame
                       + (format != null
                               ? "(parsing as '" + format + "' file)"
                               : ""),
-              oom, Desktop.desktop);
+              oom, Desktop.getDesktopPane());
+    }
+  }
+
+  /**
+   * Do all updates necessary after an annotation file such as jnet. Also called
+   * from Jalview.loadAppletParams for "annotations", "jnetFile"
+   */
+
+  public void updateForAnnotations()
+  {
+    alignPanel.adjustAnnotationHeight();
+    viewport.updateSequenceIdColours();
+    buildSortByAnnotationScoresMenu();
+    alignPanel.paintAlignment(true, true);
+  }
+
+  /**
+   * Change the display state for the given feature groups -- Added by BH from
+   * JalviewLite
+   * 
+   * @param groups
+   *          list of group strings
+   * @param state
+   *          visible or invisible
+   */
+
+  public void setFeatureGroupState(String[] groups, boolean state)
+  {
+    jalview.api.FeatureRenderer fr = null;
+    viewport.setShowSequenceFeatures(true);
+    if (alignPanel != null
+            && (fr = alignPanel.getFeatureRenderer()) != null)
+    {
+
+      fr.setGroupVisibility(Arrays.asList(groups), state);
+      alignPanel.getSeqPanel().seqCanvas.repaint();
+      if (alignPanel.overviewPanel != null)
+      {
+        alignPanel.overviewPanel.updateOverviewImage();
+      }
     }
   }
 
@@ -5066,6 +5329,7 @@ public class AlignFrame extends GAlignFrame
    * Method invoked by the ChangeListener on the tabbed pane, in other words
    * when a different tabbed pane is selected by the user or programmatically.
    */
+
   @Override
   public void tabSelectionChanged(int index)
   {
@@ -5136,6 +5400,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * On right mouse click on view tab, prompt for and set new view name.
    */
+
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
@@ -5162,6 +5427,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Open the dialog for regex description parsing.
    */
+
   @Override
   protected void extractScores_actionPerformed(ActionEvent e)
   {
@@ -5185,6 +5451,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent
    * )
    */
+
   @Override
   protected void showDbRefs_actionPerformed(ActionEvent e)
   {
@@ -5197,6 +5464,7 @@ public class AlignFrame extends GAlignFrame
    * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
    * ActionEvent)
    */
+
   @Override
   protected void showNpFeats_actionPerformed(ActionEvent e)
   {
@@ -5209,6 +5477,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param av
    */
+
   public boolean closeView(AlignViewportI av)
   {
     if (viewport == av)
@@ -5252,6 +5521,7 @@ public class AlignFrame extends GAlignFrame
             Cache.getDefault(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, true));
     trimrs.addActionListener(new ActionListener()
     {
+
       @Override
       public void actionPerformed(ActionEvent e)
       {
@@ -5273,6 +5543,7 @@ public class AlignFrame extends GAlignFrame
       {
         new Thread(new Runnable()
         {
+
           @Override
           public void run()
           {
@@ -5284,6 +5555,7 @@ public class AlignFrame extends GAlignFrame
                     alignPanel.alignFrame.featureSettings, isNucleotide);
             dbRefFetcher.addListener(new FetchFinishedListenerI()
             {
+
               @Override
               public void finished()
               {
@@ -5307,16 +5579,21 @@ public class AlignFrame extends GAlignFrame
     rfetch.add(fetchr);
     new Thread(new Runnable()
     {
+
       @Override
       public void run()
       {
-        final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
-                .getSequenceFetcherSingleton();
+        // ??
+        // final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
+        // .getSequenceFetcherSingleton();
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+
           @Override
           public void run()
           {
+            jalview.ws.SequenceFetcher sf = jalview.ws.SequenceFetcher
+                    .getInstance();
             String[] dbclasses = sf.getNonAlignmentSources();
             List<DbSourceProxy> otherdb;
             JMenu dfetch = new JMenu();
@@ -5340,9 +5617,8 @@ public class AlignFrame extends GAlignFrame
               }
               if (otherdb.size() == 1)
               {
-                final DbSourceProxy[] dassource = otherdb
-                        .toArray(new DbSourceProxy[0]);
                 DbSourceProxy src = otherdb.get(0);
+                DbSourceProxy[] dassource = new DbSourceProxy[] { src };
                 fetchr = new JMenuItem(src.getDbSource());
                 fetchr.addActionListener(new ActionListener()
                 {
@@ -5367,6 +5643,7 @@ public class AlignFrame extends GAlignFrame
                         dbRefFetcher
                                 .addListener(new FetchFinishedListenerI()
                                 {
+
                                   @Override
                                   public void finished()
                                   {
@@ -5401,6 +5678,7 @@ public class AlignFrame extends GAlignFrame
                         { src.getDbSource() }));
                 fetchr.addActionListener(new ActionListener()
                 {
+
                   @Override
                   public void actionPerformed(ActionEvent e)
                   {
@@ -5421,6 +5699,7 @@ public class AlignFrame extends GAlignFrame
                         dbRefFetcher
                                 .addListener(new FetchFinishedListenerI()
                                 {
+
                                   @Override
                                   public void finished()
                                   {
@@ -5489,6 +5768,7 @@ public class AlignFrame extends GAlignFrame
                           dbRefFetcher
                                   .addListener(new FetchFinishedListenerI()
                                   {
+
                                     @Override
                                     public void finished()
                                     {
@@ -5540,23 +5820,23 @@ public class AlignFrame extends GAlignFrame
   /**
    * Left justify the whole alignment.
    */
+
   @Override
   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
   {
-    AlignmentI al = viewport.getAlignment();
-    al.justify(false);
-    viewport.firePropertyChange("alignment", null, al);
+    viewport.getAlignment().justify(false);
+    viewport.notifyAlignment();
   }
 
   /**
    * Right justify the whole alignment.
    */
+
   @Override
   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
   {
-    AlignmentI al = viewport.getAlignment();
-    al.justify(true);
-    viewport.firePropertyChange("alignment", null, al);
+    viewport.getAlignment().justify(true);
+    viewport.notifyAlignment();
   }
 
   @Override
@@ -5573,6 +5853,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
    * awt.event.ActionEvent)
    */
+
   @Override
   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
   {
@@ -5587,6 +5868,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
    * .ActionEvent)
    */
+
   @Override
   protected void showGroupConsensus_actionPerformed(ActionEvent e)
   {
@@ -5602,6 +5884,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
    * .event.ActionEvent)
    */
+
   @Override
   protected void showGroupConservation_actionPerformed(ActionEvent e)
   {
@@ -5616,6 +5899,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
    * .event.ActionEvent)
    */
+
   @Override
   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
   {
@@ -5630,6 +5914,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
    * .event.ActionEvent)
    */
+
   @Override
   protected void showSequenceLogo_actionPerformed(ActionEvent e)
   {
@@ -5659,6 +5944,7 @@ public class AlignFrame extends GAlignFrame
    * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
    * .event.ActionEvent)
    */
+
   @Override
   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
   {
@@ -5710,6 +5996,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param alignmentPanel
    */
+
   public void setDisplayedView(AlignmentPanel alignmentPanel)
   {
     if (!viewport.getSequenceSetId()
@@ -5734,6 +6021,7 @@ public class AlignFrame extends GAlignFrame
    * @param forAlignment
    *          update non-sequence-related annotations
    */
+
   @Override
   protected void setAnnotationsVisibility(boolean visible,
           boolean forSequences, boolean forAlignment)
@@ -5767,6 +6055,7 @@ public class AlignFrame extends GAlignFrame
   /**
    * Store selected annotation sort order for the view and repaint.
    */
+
   @Override
   protected void sortAnnotations_actionPerformed()
   {
@@ -5780,6 +6069,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @return alignment panels in this alignment frame
    */
+
   public List<? extends AlignmentViewPanel> getAlignPanels()
   {
     // alignPanels is never null
@@ -5791,6 +6081,7 @@ public class AlignFrame extends GAlignFrame
    * Open a new alignment window, with the cDNA associated with this (protein)
    * alignment, aligned as is the protein.
    */
+
   protected void viewAsCdna_actionPerformed()
   {
     // TODO no longer a menu action - refactor as required
@@ -5841,6 +6132,7 @@ public class AlignFrame extends GAlignFrame
    * 
    * @param show
    */
+
   @Override
   protected void showComplement_actionPerformed(boolean show)
   {
@@ -5855,6 +6147,7 @@ public class AlignFrame extends GAlignFrame
    * Generate the reverse (optionally complemented) of the selected sequences,
    * and add them to the alignment
    */
+
   @Override
   protected void showReverse_actionPerformed(boolean complement)
   {
@@ -5880,6 +6173,7 @@ public class AlignFrame extends GAlignFrame
    * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
    * be targeted at this alignment.
    */
+
   @Override
   protected void runGroovy_actionPerformed()
   {
@@ -5893,7 +6187,7 @@ public class AlignFrame extends GAlignFrame
       } catch (Exception ex)
       {
         System.err.println((ex.toString()));
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.couldnt_run_groovy_script"),
                 MessageManager.getString("label.groovy_support_failed"),
                 JvOptionPane.ERROR_MESSAGE);
@@ -5913,6 +6207,7 @@ public class AlignFrame extends GAlignFrame
    * @param columnsContaining
    * @return
    */
+
   public boolean hideFeatureColumns(String featureType,
           boolean columnsContaining)
   {
@@ -5945,6 +6240,7 @@ public class AlignFrame extends GAlignFrame
    * Rebuilds the Colour menu, including any user-defined colours which have
    * been loaded either on startup or during the session
    */
+
   public void buildColourMenu()
   {
     colourMenu.removeAll();
@@ -5972,6 +6268,7 @@ public class AlignFrame extends GAlignFrame
    * Open a dialog (if not already open) that allows the user to select and
    * calculate PCA or Tree analysis
    */
+
   protected void openTreePcaDialog()
   {
     if (alignPanel.getCalculationDialog() == null)
@@ -5999,6 +6296,7 @@ public class AlignFrame extends GAlignFrame
     final AlignFrame us = this;
     chooser.setResponseHandler(0, new Runnable()
     {
+
       @Override
       public void run()
       {
@@ -6025,42 +6323,110 @@ public class AlignFrame extends GAlignFrame
   {
     return lastFeatureSettingsBounds;
   }
-}
 
-class PrintThread extends Thread
-{
-  AlignmentPanel ap;
+  public void scrollTo(int row, int column)
+  {
+    alignPanel.getSeqPanel().scrollTo(row, column);
+  }
 
-  public PrintThread(AlignmentPanel ap)
+  public void scrollToRow(int row)
   {
-    this.ap = ap;
+    alignPanel.getSeqPanel().scrollToRow(row);
   }
 
-  static PageFormat pf;
+  public void scrollToColumn(int column)
+  {
+    alignPanel.getSeqPanel().scrollToColumn(column);
+  }
 
-  @Override
-  public void run()
+  /**
+   * BH 2019 from JalviewLite
+   * 
+   * get sequence feature groups that are hidden or shown
+   * 
+   * @param visible
+   *          true is visible
+   * @return list
+   */
+
+  public String[] getFeatureGroupsOfState(boolean visible)
   {
-    PrinterJob printJob = PrinterJob.getPrinterJob();
+    jalview.api.FeatureRenderer fr = null;
+    if (alignPanel != null
+            && (fr = alignPanel.getFeatureRenderer()) != null)
+    {
+      List<String> gps = fr.getGroups(visible);
+      String[] _gps = gps.toArray(new String[gps.size()]);
+      return _gps;
+    }
+    return null;
+  }
 
-    if (pf != null)
+  /**
+   * 
+   * @return list of feature groups on the view
+   */
+
+  public String[] getFeatureGroups()
+  {
+    jalview.api.FeatureRenderer fr = null;
+    if (alignPanel != null
+            && (fr = alignPanel.getFeatureRenderer()) != null)
     {
-      printJob.setPrintable(ap, pf);
+      List<String> gps = fr.getFeatureGroups();
+      String[] _gps = gps.toArray(new String[gps.size()]);
+      return _gps;
     }
-    else
+    return null;
+  }
+
+  public void select(SequenceGroup sel, ColumnSelection csel,
+          HiddenColumns hidden)
+  {
+    alignPanel.getSeqPanel().selection(sel, csel, hidden, null);
+  }
+
+  public int getID()
+  {
+    return id;
+  }
+
+  static class PrintThread extends Thread
+  {
+    AlignmentPanel ap;
+
+    public PrintThread(AlignmentPanel ap)
     {
-      printJob.setPrintable(ap);
+      this.ap = ap;
     }
 
-    if (printJob.printDialog())
+    static PageFormat pf;
+
+    @Override
+    public void run()
     {
-      try
+      PrinterJob printJob = PrinterJob.getPrinterJob();
+
+      if (pf != null)
       {
-        printJob.print();
-      } catch (Exception PrintException)
+        printJob.setPrintable(ap, pf);
+      }
+      else
+      {
+        printJob.setPrintable(ap);
+      }
+
+      if (printJob.printDialog())
       {
-        PrintException.printStackTrace();
+        try
+        {
+          printJob.print();
+        } catch (Exception PrintException)
+        {
+          PrintException.printStackTrace();
+        }
       }
     }
   }
 }
+
index 6024cf9..dca9047 100644 (file)
@@ -74,6 +74,14 @@ import javax.swing.JInternalFrame;
 public class AlignViewport extends AlignmentViewport
         implements SelectionSource
 {
+
+  public final static int NO_SPLIT = 0;
+
+  public final static int SPLIT_FRAME = 1;
+
+  public final static int NEW_WINDOW = 2;
+
+
   Font font;
 
   boolean cursorMode = false;
@@ -163,7 +171,7 @@ public class AlignViewport extends AlignmentViewport
    * @param hiddenColumns
    * @param seqsetid
    *          (may be null)
-   */
+f   */
   public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns,
           String seqsetid)
   {
@@ -216,7 +224,7 @@ public class AlignViewport extends AlignmentViewport
 
     setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false));
     setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false));
-    autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
+    autoCalculateConsensusAndConservation = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
 
     setPadGaps(Cache.getDefault("PAD_GAPS", true));
     setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
@@ -392,7 +400,7 @@ public class AlignViewport extends AlignmentViewport
     if (align != null)
     {
       StructureSelectionManager ssm = StructureSelectionManager
-              .getStructureSelectionManager(Desktop.instance);
+              .getStructureSelectionManager(Desktop.getInstance());
       ssm.registerMappings(align.getCodonFrames());
     }
 
@@ -414,7 +422,7 @@ public class AlignViewport extends AlignmentViewport
       if (mappings != null)
       {
         StructureSelectionManager ssm = StructureSelectionManager
-                .getStructureSelectionManager(Desktop.instance);
+                .getStructureSelectionManager(Desktop.getInstance());
         for (AlignedCodonFrame acf : mappings)
         {
           if (noReferencesTo(acf))
@@ -541,7 +549,7 @@ public class AlignViewport extends AlignmentViewport
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance)
+            .getStructureSelectionManager(Desktop.getInstance())
             .sendSelection(new SequenceGroup(getSelectionGroup()),
                     new ColumnSelection(getColumnSelection()),
                     new HiddenColumns(getAlignment().getHiddenColumns()),
@@ -587,7 +595,7 @@ public class AlignViewport extends AlignmentViewport
   public StructureSelectionManager getStructureSelectionManager()
   {
     return StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
   }
   
   @Override
@@ -755,7 +763,8 @@ public void setNormaliseSequenceLogo(boolean state)
     }
 
     ranges.setEndSeq(getAlignment().getHeight() - 1); // BH 2019.04.18
-    firePropertyChange("alignment", null, getAlignment().getSequences());
+    notifyAlignment();
+
   }
 
   /**
@@ -781,48 +790,55 @@ public void setNormaliseSequenceLogo(boolean state)
      * dialog responses 0, 1, 2 (even though JOptionPane shows them
      * in reverse order)
      */
-    JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop)
-            .setResponseHandler(0, new Runnable()
+    JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane())
+            .setResponseHandler(NO_SPLIT, new Runnable()
             {
               @Override
               public void run()
               {
                   addDataToAlignment(al);
               }
-            }).setResponseHandler(1, new Runnable()
+            }).setResponseHandler(SPLIT_FRAME, new Runnable()
             {
               @Override
               public void run()
               {
-                us.openLinkedAlignmentAs(al, title, true);
+                openLinkedAlignmentAs(getAlignPanel().alignFrame,
+                        new Alignment(getAlignment()), al, title,
+                        SPLIT_FRAME);
+//                us.openLinkedAlignmentAs(al, title, true);
               }
-            }).setResponseHandler(2, new Runnable()
+            }).setResponseHandler(NEW_WINDOW, new Runnable()
             {
               @Override
               public void run()
               {
-                us.openLinkedAlignmentAs(al, title, false);
+                openLinkedAlignmentAs(null, getAlignment(), al, title,
+                        NEW_WINDOW);
               }
             });
-       dialog.showDialog(question,
+      dialog.showDialog(question,
             MessageManager.getString("label.open_split_window"),
             JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
             options, options[0]);
   }
-
-  protected void openLinkedAlignmentAs(AlignmentI al, String title,
-          boolean newWindowOrSplitPane)
-    {
+  /**
+   * Open a split frame or a new window
+   * 
+   * @param al
+   * @param title
+   * @param mode
+   *          SPLIT_FRAME or NEW_WINDOW
+   */
+  public static void openLinkedAlignmentAs(AlignFrame thisFrame,
+          AlignmentI thisAlignment, AlignmentI al, String title, int mode)
+  {
     /*
      * Identify protein and dna alignments. Make a copy of this one if opening
      * in a new split pane.
      */
-    AlignmentI thisAlignment = newWindowOrSplitPane
-            ? new Alignment(getAlignment())
-            : getAlignment();
     AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
-    final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
-
+    AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
     /*
      * Map sequences. At least one should get mapped as we have already passed
      * the test for 'mappability'. Any mappings made will be added to the
@@ -850,7 +866,7 @@ public void setNormaliseSequenceLogo(boolean state)
     // alignFrame.setFileName(file, format);
     // }
 
-    if (!newWindowOrSplitPane)
+    if (mode == NEW_WINDOW)
     {
       Desktop.addInternalFrame(newAlignFrame, title,
               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
@@ -864,10 +880,10 @@ public void setNormaliseSequenceLogo(boolean state)
     {
     }
 
-    if (newWindowOrSplitPane)
+    if (mode == SPLIT_FRAME)
     {
       al.alignAs(thisAlignment);
-      protein = openSplitFrame(newAlignFrame, thisAlignment);
+      openSplitFrame(thisFrame, newAlignFrame, thisAlignment);
     }
   }
 
@@ -881,8 +897,8 @@ public void setNormaliseSequenceLogo(boolean state)
    *          cdna/protein complement alignment to show in the other split half
    * @return the protein alignment in the split frame
    */
-  protected AlignmentI openSplitFrame(AlignFrame newAlignFrame,
-          AlignmentI complement)
+  static protected AlignmentI openSplitFrame(AlignFrame thisFrame,
+          AlignFrame newAlignFrame, AlignmentI complement)
   {
     /*
      * Make a new frame with a copy of the alignment we are adding to. If this
@@ -891,7 +907,8 @@ public void setNormaliseSequenceLogo(boolean state)
      */
     AlignFrame copyMe = new AlignFrame(complement, AlignFrame.DEFAULT_WIDTH,
             AlignFrame.DEFAULT_HEIGHT);
-    copyMe.setTitle(getAlignPanel().alignFrame.getTitle());
+    copyMe.setTitle(thisFrame.getTitle());
+
 
     AlignmentI al = newAlignFrame.viewport.getAlignment();
     final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
@@ -1148,4 +1165,5 @@ public void setNormaliseSequenceLogo(boolean state)
   {
     this.viewName = viewName;
   }
+
 }
index 45e4b95..277e11d 100644 (file)
  */
 package jalview.gui;
 
-import jalview.analysis.AnnotationSorter;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Cache;
-import jalview.bin.Jalview;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.SearchResultsI;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.gui.ImageExporter.ImageWriterI;
-import jalview.io.HTMLOutput;
-import jalview.jbgui.GAlignmentPanel;
-import jalview.math.AlignmentDimension;
-import jalview.schemes.ResidueProperties;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.Comparison;
-import jalview.util.ImageMaker;
-import jalview.util.MessageManager;
-import jalview.viewmodel.ViewportListenerI;
-import jalview.viewmodel.ViewportRanges;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Container;
@@ -67,6 +44,30 @@ import java.util.List;
 
 import javax.swing.SwingUtilities;
 
+import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.gui.ImageExporter.ImageWriterI;
+import jalview.io.HTMLOutput;
+import jalview.jbgui.GAlignmentPanel;
+import jalview.math.AlignmentDimension;
+import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.Comparison;
+import jalview.util.ImageMaker;
+import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+
 /**
  * DOCUMENT ME!
  * 
@@ -122,7 +123,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public AlignmentPanel(AlignFrame af, final AlignViewport av)
   {
-//     setBackground(Color.white);  // BH 2019
+    setName("AligmentPanel");
+    // setBackground(Color.white); // BH 2019
     alignFrame = af;
     this.av = av;
     setSeqPanel(new SeqPanel(av, this));
@@ -173,6 +175,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           ranges.setViewportWidth(widthInRes);
           ranges.setViewportHeight(heightInSeq);
         }
+        repaint();
       }
 
     });
@@ -183,10 +186,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
       @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
-        if (evt.getPropertyName().equals("alignment"))
-        {
+        switch (evt.getPropertyName()) {  
+        case AlignmentViewport.PROPERTY_SEQUENCE:
+          updateScrollBarsFromRanges();
+          if (annotationPanel != null)
+            annotationPanel.paintImmediately(0,  0, getWidth(), getHeight());
+          break;
+        case AlignmentViewport.PROPERTY_ALIGNMENT:
+          updateScrollBarsFromRanges();
           PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
           alignmentChanged();
+          break;
         }
       }
     };
@@ -259,32 +269,37 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int oldWidth = av.getIdWidth();
 
     // calculate sensible default width when no preference is available
-    Dimension r = null;
+    Dimension d = null;
     if (av.getIdWidth() < 0)
     {
-      int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
-      int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3);
-      int maxwidth = Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth);
-      r = calculateIdWidth(maxwidth);
-      av.setIdWidth(r.width);
+      int maxWidth = getMaxWidth();
+      d = calculateIdWidth(maxWidth);
+      av.setIdWidth(d.width);
     }
     else
     {
-      r = new Dimension();
-      r.width = av.getIdWidth();
-      r.height = 0;
+      d = new Dimension();
+      d.width = av.getIdWidth();
+      d.height = 0;
     }
 
     /*
      * fudge: if desired width has changed, update layout
      * (see also paintComponent - updates layout on a repaint)
      */
-    if (r.width != oldWidth)
+    if (d.width != oldWidth)
     {
-      idPanelHolder.setPreferredSize(r);
+      idPanelHolder.setPreferredSize(d);
       validate();
     }
-    return r;
+    return d;
+  }
+
+  public int getMaxWidth()
+  {
+    int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
+    int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3);
+    return Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth);
   }
 
   /**
@@ -296,10 +311,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @return Dimension giving the maximum width of the alignment label panel
    *         that should be used.
    */
-  protected Dimension calculateIdWidth(int maxwidth)
+  public Dimension calculateIdWidth(int maxwidth)
   {
-    Container c = new Container();
-
+    Container c = this;// new Container();
     FontMetrics fm = c.getFontMetrics(
             new Font(av.font.getName(), Font.ITALIC, av.font.getSize()));
 
@@ -307,10 +321,12 @@ public class AlignmentPanel extends GAlignmentPanel implements
     int i = 0;
     int idWidth = 0;
 
+    boolean withSuffix = av.getShowJVSuffix();
+
     while ((i < al.getHeight()) && (al.getSequenceAt(i) != null))
     {
       SequenceI s = al.getSequenceAt(i);
-      String id = s.getDisplayId(av.getShowJVSuffix());
+      String id = s.getDisplayId(withSuffix);
       int stringWidth = fm.stringWidth(id);
       idWidth = Math.max(idWidth, stringWidth);
       i++;
@@ -471,8 +487,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
         /*
          * Scroll down to make end of search results visible
          */
-        setScrollValues(ranges.getStartRes(), starts + seqIndex - ends
-                + 1);
+        setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1);
       }
       /*
        * Else results are already visible - no need to scroll
@@ -554,17 +569,24 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     // BH 2018.04.18 comment: addNotify() is not appropriate here. We
     // are not changing ancestors, and keyboard action listeners do
-    // not need to be reset. addNotify() is a very expensive operation,
+    // not need to be reset, and most importantly, we can't be sure we are actually
+    // connected to resources. 
+    
+    // addNotify() is a very expensive operation,
     // requiring a full re-layout of all parents and children.
+    
     // Note in JComponent:
+    
     // This method is called by the toolkit internally and should
     // not be called directly by programs.
+    
     // I note that addNotify() is called in several areas of Jalview.
 
     int annotationHeight = getAnnotationPanel().adjustPanelHeight();
     annotationHeight = getAnnotationPanel()
             .adjustForAlignFrame(adjustPanelHeight, annotationHeight);
 
+    // BH no!!
     hscroll.addNotify();
     annotationScroller.setPreferredSize(
             new Dimension(annotationScroller.getWidth(), annotationHeight));
@@ -572,7 +594,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
     Dimension e = idPanel.getSize();
     alabels.setSize(new Dimension(e.width, annotationHeight));
 
-
     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
             annotationSpaceFillerHolder.getWidth(), annotationHeight));
     annotationScroller.validate();
@@ -581,16 +602,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   /**
    * update alignment layout for viewport settings
-   * 
-   * @param wrap
-   *          DOCUMENT ME!
    */
   public void updateLayout()
   {
+    ViewportRanges ranges = av.getRanges();
     fontChanged();
     setAnnotationVisible(av.isShowAnnotation());
     boolean wrap = av.getWrapAlignment();
-    ViewportRanges ranges = av.getRanges();
     ranges.setStartSeq(0);
     scalePanelHolder.setVisible(!wrap);
     hscroll.setVisible(!wrap);
@@ -605,9 +623,24 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       annotationScroller.setVisible(true);
       annotationSpaceFillerHolder.setVisible(true);
-      validateAnnotationDimensions(false);
     }
 
+    idSpaceFillerPanel1.setVisible(!wrap);
+
+    /*
+     * defer dimension calculations if panel not yet added to a Window
+     * BH 2020.06.09
+     */
+    if (getTopLevelAncestor() == null)
+    {
+      repaint();
+      return;
+    }
+
+    if (!wrap && av.isShowAnnotation())
+    {
+      validateAnnotationDimensions(false);
+    }
     int canvasWidth = getSeqPanel().seqCanvas.getWidth();
     if (canvasWidth > 0)
     { // may not yet be laid out
@@ -628,7 +661,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
     }
 
-    idSpaceFillerPanel1.setVisible(!wrap);
+    // System.out.println("ap dim = " + getSize());
+    // these values will go negative if getSize() returns (0,0):
+    // System.out.println("seqpan dim = " + getSeqPanel().getSize());
+    // System.out.println("seqcan dim = " + getSeqPanel().seqCanvas.getSize());
 
     repaint();
   }
@@ -642,10 +678,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          visible row to scroll to
    * 
    */
-  public void setScrollValues(int xpos, int ypos)
+  public void setScrollValues(int x, int y)
   {
-    int x = xpos;
-    int y = ypos;
 
     if (av == null || av.getAlignment() == null)
     {
@@ -660,46 +694,24 @@ public class AlignmentPanel extends GAlignmentPanel implements
     {
       int width = av.getAlignment().getVisibleWidth();
       int height = av.getAlignment().getHeight();
-
-      hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
-      vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
-
-      if (hextent > width)
-      {
-        hextent = width;
-      }
-
-      if (vextent > height)
-      {
-        vextent = height;
-      }
-
-      if ((hextent + x) > width)
-      {
-        x = width - hextent;
-      }
-
-      if ((vextent + y) > height)
-      {
-        y = height - vextent;
-      }
-
-      if (y < 0)
-      {
-        y = 0;
-      }
-
-      if (x < 0)
-      {
-        x = 0;
-      }
-
-      // update the scroll values
-      hscroll.setValues(x, hextent, 0, width);
-      vscroll.setValues(y, vextent, 0, height);
+      
+      hextent = Math.min(getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(),  width);
+      vextent = Math.min(getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(),  height);
+  
+      x = Math.max(0, Math.min(x,  width - hextent));
+      y = Math.max(0, Math.min(y,  height - vextent));
+      
+      updateRanges(x, y);
+      updateScrollBars(x, y, width, height);
     }
   }
 
+  private void updateScrollBars(int x, int y, int width, int height) 
+  {
+    hscroll.setValues(x, hextent, 0, width);
+    vscroll.setValues(y, vextent, 0, height);
+  }
+
   /**
    * Respond to adjustment event when horizontal or vertical scrollbar is
    * changed
@@ -716,41 +728,54 @@ public class AlignmentPanel extends GAlignmentPanel implements
       return;
     }
 
-    ViewportRanges ranges = av.getRanges();
-
     if (evt.getSource() == hscroll)
     {
+      if (!updateRanges(hscroll.getValue(), Integer.MIN_VALUE))
+        return;
+    }
+    else if (evt.getSource() == vscroll)
+    {
+      if (!updateRanges(Integer.MIN_VALUE, vscroll.getValue()))
+        return;
+    }
+    repaint();
+  }
+
+  private boolean updateRanges(int x, int y)
+  {
+    ViewportRanges ranges = av.getRanges();
+    boolean isChanged = false;
+    if (x != Integer.MIN_VALUE)
+    {
       int oldX = ranges.getStartRes();
       int oldwidth = ranges.getViewportWidth();
-      int x = hscroll.getValue();
       int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
 
       // if we're scrolling to the position we're already at, stop
       // this prevents infinite recursion of events when the scroll/viewport
       // ranges values are the same
-      if ((x == oldX) && (width == oldwidth))
+      if (width > 0 && (x != oldX || width != oldwidth))
       {
-        return;
+        ranges.setViewportStartAndWidth(x, width);
+        isChanged = true;
       }
-      ranges.setViewportStartAndWidth(x, width);
     }
-    else if (evt.getSource() == vscroll)
+    if (y != Integer.MIN_VALUE)
     {
       int oldY = ranges.getStartSeq();
       int oldheight = ranges.getViewportHeight();
-      int y = vscroll.getValue();
       int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
 
       // if we're scrolling to the position we're already at, stop
       // this prevents infinite recursion of events when the scroll/viewport
       // ranges values are the same
-      if ((y == oldY) && (height == oldheight))
+      if (height > 0 && (y != oldY || height != oldheight))
       {
-        return;
+        ranges.setViewportStartAndHeight(y, height);
+        isChanged = true;
       }
-      ranges.setViewportStartAndHeight(y, height);
     }
-    repaint();
+    return isChanged;
   }
 
   /**
@@ -829,7 +854,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
             av.isShowAutocalculatedAbove());
     sorter.sort(getAlignment().getAlignmentAnnotation(),
             av.getSortAnnotationsBy());
-    repaint();
 
     if (updateStructures)
     {
@@ -837,17 +861,21 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
     if (updateOverview)
     {
-
+      alignFrame.repaint();
       if (overviewPanel != null)
       {
         overviewPanel.updateOverviewImage();
       }
+    } else {
+      invalidate(); // needed so that the id width adjuster works correctly
+      repaint();
     }
   }
 
   @Override
   public void paintComponent(Graphics g)
   {
+    // BH OUCH!
     invalidate(); // needed so that the id width adjuster works correctly
 
     Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
@@ -861,7 +889,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * though I still think this call should be elsewhere.
      */
     ViewportRanges ranges = av.getRanges();
-    setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
+    // setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
     super.paintComponent(g);
   }
 
@@ -1020,8 +1048,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * single graphics context), then reset to (0, scale height)
      */
     alignmentGraphics.translate(alignmentGraphicsOffset, scaleHeight);
-    getSeqPanel().seqCanvas.drawPanelForPrinting(alignmentGraphics, startRes,
-            endRes, startSeq, endSeq - 1);
+    getSeqPanel().seqCanvas.drawPanelForPrinting(alignmentGraphics,
+            startRes, endRes, startSeq, endSeq - 1);
     alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
 
     if (av.isShowAnnotation() && (endSeq == alignmentHeight))
@@ -1065,11 +1093,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * 
    * @throws PrinterException
    */
-  public int printWrappedAlignment(int pageWidth, int pageHeight, int pageNumber,
-          Graphics g) throws PrinterException
+  public int printWrappedAlignment(int pageWidth, int pageHeight,
+          int pageNumber, Graphics g) throws PrinterException
   {
-    getSeqPanel().seqCanvas.calculateWrappedGeometry(getWidth(),
-            getHeight());
+    getSeqPanel().seqCanvas.calculateWrappedGeometry();
     int annotationHeight = 0;
     if (av.isShowAnnotation())
     {
@@ -1118,8 +1145,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     g.translate(idWidth, 0);
 
-    getSeqPanel().seqCanvas.drawWrappedPanelForPrinting(g, pageWidth - idWidth,
-            totalHeight, 0);
+    getSeqPanel().seqCanvas.drawWrappedPanelForPrinting(g,
+            pageWidth - idWidth, totalHeight, 0);
 
     if ((pageNumber * pageHeight) < totalHeight)
     {
@@ -1284,13 +1311,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
             String triplet = null;
             if (av.getAlignment().isNucleotide())
             {
-              triplet = ResidueProperties.nucleotideName.get(seq
-                      .getCharAt(column) + "");
+              triplet = ResidueProperties.nucleotideName
+                      .get(seq.getCharAt(column) + "");
             }
             else
             {
-              triplet = ResidueProperties.aa2Triplet.get(seq.getCharAt(column)
-                      + "");
+              triplet = ResidueProperties.aa2Triplet
+                      .get(seq.getCharAt(column) + "");
             }
 
             if (triplet == null)
@@ -1307,7 +1334,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
                 text.append("<area shape=\"rect\" coords=\"")
                         .append((idWidth + column * av.getCharWidth()))
                         .append(",").append(sy).append(",")
-                        .append((idWidth + (column + 1) * av.getCharWidth()))
+                        .append((idWidth
+                                + (column + 1) * av.getCharWidth()))
                         .append(",").append((av.getCharHeight() + sy))
                         .append("\"").append(" onMouseOver=\"toolTip('")
                         .append(seqPos).append(" ").append(triplet);
@@ -1333,7 +1361,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
             }
             if (!Comparison.isGap(seq.getCharAt(column)))
             {
-              List<SequenceFeature> features = seq.findFeatures(column, column);
+              List<SequenceFeature> features = seq.findFeatures(column,
+                      column);
               for (SequenceFeature sf : features)
               {
                 if (sf.isContactFeature())
@@ -1702,10 +1731,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
   public void propertyChange(PropertyChangeEvent evt)
   {
     // update this panel's scroll values based on the new viewport ranges values
-    ViewportRanges ranges = av.getRanges();
-    int x = ranges.getStartRes();
-    int y = ranges.getStartSeq();
-    setScrollValues(x, y);
+    updateScrollBarsFromRanges();
 
     // now update any complementary alignment (its viewport ranges object
     // is different so does not get automatically updated)
@@ -1717,6 +1743,12 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
   }
 
+  void updateScrollBarsFromRanges()
+  {
+    ViewportRanges ranges = av.getRanges();
+    setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
+  }
+
   /**
    * Set the reference to the PCA/Tree chooser dialog for this panel. This
    * reference should be nulled when the dialog is closed.
@@ -1737,4 +1769,201 @@ public class AlignmentPanel extends GAlignmentPanel implements
     return calculationDialog;
   }
 
+  /**
+   * From appletgui, for JalviewJS JavaScript interface
+   * 
+   * preliminary - untested
+   * 
+   * @param ostart
+   * @param end
+   * @param seqIndex
+   * @param scrollToNearest
+   * @param redrawOverview
+   * @return
+   */
+  public boolean scrollTo(int ostart, int end, int seqIndex,
+          boolean scrollToNearest, boolean redrawOverview)
+  {
+    int startv, endv, starts, ends;// , width;
+
+    int start = -1;
+    if (av.hasHiddenColumns())
+    {
+      AlignmentI al = av.getAlignment();
+      start = al.getHiddenColumns().absoluteToVisibleColumn(ostart);
+      end = al.getHiddenColumns().absoluteToVisibleColumn(end);
+      if (start == end)
+      {
+        if (!scrollToNearest && !al.getHiddenColumns().isVisible(ostart))
+        {
+          // don't scroll - position isn't visible
+          return false;
+        }
+      }
+    }
+    else
+    {
+      start = ostart;
+    }
+
+    ViewportRanges ranges = av.getRanges();
+    if (!av.getWrapAlignment())
+    {
+      /*
+       * int spos=av.getStartRes(),sqpos=av.getStartSeq(); if ((startv =
+       * av.getStartRes()) >= start) { spos=start-1; // seqIn //
+       * setScrollValues(start - 1, seqIndex); } else if ((endv =
+       * av.getEndRes()) <= end) { // setScrollValues(spos=startv + 1 + end -
+       * endv, seqIndex); spos=startv + 1 + end - endv; } else if ((starts =
+       * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(),
+       * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) {
+       * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); }
+       */
+
+      // below is scrolling logic up to Jalview 2.8.2
+      // if ((av.getStartRes() > end)
+      // || (av.getEndRes() < start)
+      // || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex)))
+      // {
+      // if (start > av.getAlignment().getWidth() - hextent)
+      // {
+      // start = av.getAlignment().getWidth() - hextent;
+      // if (start < 0)
+      // {
+      // start = 0;
+      // }
+      //
+      // }
+      // if (seqIndex > av.getAlignment().getHeight() - vextent)
+      // {
+      // seqIndex = av.getAlignment().getHeight() - vextent;
+      // if (seqIndex < 0)
+      // {
+      // seqIndex = 0;
+      // }
+      // }
+      // setScrollValues(start, seqIndex);
+      // }
+      // logic copied from jalview.gui.AlignmentPanel:
+      if ((startv = ranges.getStartRes()) >= start)
+      {
+        /*
+         * Scroll left to make start of search results visible
+         */
+        setScrollValues(start - 1, seqIndex);
+      }
+      else if ((endv = ranges.getEndRes()) <= end)
+      {
+        /*
+         * Scroll right to make end of search results visible
+         */
+        setScrollValues(startv + 1 + end - endv, seqIndex);
+      }
+      else if ((starts = ranges.getStartSeq()) > seqIndex)
+      {
+        /*
+         * Scroll up to make start of search results visible
+         */
+        setScrollValues(ranges.getStartRes(), seqIndex);
+      }
+      else if ((ends = ranges.getEndSeq()) <= seqIndex)
+      {
+        /*
+         * Scroll down to make end of search results visible
+         */
+        setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1);
+      }
+      /*
+       * Else results are already visible - no need to scroll
+       */
+    }
+    else
+    {
+      ranges.scrollToWrappedVisible(start);
+    }
+
+    paintAlignment(redrawOverview, false);
+    return true;
+  }
+
+  private boolean holdRepaint = false;
+
+  /**
+   * Called by IdCanvas and SeqPanel to defer painting until after JVP loading.
+   * 
+   * @return true if holding
+   */
+  public boolean getHoldRepaint()
+  {
+    return holdRepaint;
+  }
+
+  /**
+   * Called by Jalview2xml while loading
+   * 
+   * @param tf
+   */
+  public void setHoldRepaint(boolean tf)
+  {
+    if (holdRepaint == tf)
+    {
+      return;
+    }
+    holdRepaint = tf;
+    if (!tf)
+    {
+      repaint();
+    }
+  }
+
+  @Override
+  public void repaint()
+  {
+    if (holdRepaint)
+    {
+      // System.out.println("AP repaint holding");
+      // Platform.stackTrace();
+      return;
+    }
+    super.repaint();
+  }
+
+  public void selectAllSequences()
+  {
+    selectSequences(av.getAlignment().getSequences());
+  }
+
+  public void deselectAllSequences()
+  {
+    if (av.cursorMode)
+    {
+      getSeqPanel().keyboardNo1 = null;
+      getSeqPanel().keyboardNo2 = null;
+    }
+    av.setSelectionGroup(null);
+    av.getColumnSelection().clear();
+    av.setSearchResults(null);
+    getIdPanel().getIdCanvas().searchResults = null;
+    av.sendSelection();
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    paintAlignment(false, false);
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+  }
+
+  public void selectSequences(List<SequenceI> seqs)
+  {
+    SequenceGroup sg = new SequenceGroup(seqs);
+    sg.setEndRes(av.getAlignment().getWidth() - 1);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+    av.sendSelection();
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    paintAlignment(false, false);
+    PaintRefresher.Refresh(this, av.getSequenceSetId());
+  }
+
 }
index 791421d..233f280 100644 (file)
@@ -605,8 +605,8 @@ public class AnnotationChooser extends JPanel
     frame.setContentPane(this);
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     Desktop.addInternalFrame(frame,
-            MessageManager.getString("label.choose_annotations"),
-            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
+            MessageManager.getString("label.choose_annotations"), Desktop.FRAME_MAKE_VISIBLE,
+            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
   }
 
   protected void setShowSelected(boolean showSelected)
index e89c1c2..bd05be1 100644 (file)
@@ -301,7 +301,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
         updateView();
       }
     };
-    JalviewColourChooser.showColourChooser(Desktop.getDesktop(), ttl,
+    JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(), ttl,
             colourPanel.getBackground(), listener);
   }
 
index d84287f..541c63a 100644 (file)
@@ -109,8 +109,8 @@ public class AnnotationExporter extends JPanel
     frame.setContentPane(this);
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     Dimension preferredSize = frame.getPreferredSize();
-    Desktop.addInternalFrame(frame, "", true, preferredSize.width,
-            preferredSize.height, true, true);
+    Desktop.addInternalFrame(frame, "", Desktop.FRAME_MAKE_VISIBLE, preferredSize.width,
+            preferredSize.height, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_ALLOW_ANY_SIZE);
   }
 
   /**
index 461a3f1..673f04d 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.analysis.AlignSeq;
-import jalview.analysis.AlignmentUtils;
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.io.FileFormat;
-import jalview.io.FormatAdapter;
-import jalview.util.Comparison;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.workers.InformationThread;
-
 import java.awt.Color;
 import java.awt.Cursor;
 import java.awt.Dimension;
@@ -63,6 +47,22 @@ import javax.swing.JPopupMenu;
 import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
+import jalview.analysis.AlignSeq;
+import jalview.analysis.AlignmentUtils;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.io.FileFormat;
+import jalview.io.FormatAdapter;
+import jalview.util.Comparison;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.workers.InformationThread;
+
 /**
  * The panel that holds the labels for alignment annotations, providing
  * tooltips, context menus, drag to reorder rows, and drag to adjust panel
@@ -1134,7 +1134,7 @@ public class AnnotationLabels extends JPanel
             seqs, omitHidden, alignmentStartEnd);
 
     Toolkit.getDefaultToolkit().getSystemClipboard()
-            .setContents(new StringSelection(output), Desktop.instance);
+            .setContents(new StringSelection(output), Desktop.getInstance());
 
     HiddenColumns hiddenColumns = null;
 
@@ -1144,20 +1144,12 @@ public class AnnotationLabels extends JPanel
               av.getAlignment().getHiddenColumns());
     }
 
-    Desktop.jalviewClipboard = new Object[] { seqs, ds, // what is the dataset
-                                                        // of a consensus
-                                                        // sequence ? need to
-                                                        // flag
-        // sequence as special.
+    // what is the dataset of a consensus sequence? 
+    // need to flag sequence as special.
+    Desktop.getInstance().jalviewClipboard = new Object[] { seqs, ds, 
         hiddenColumns };
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param g1
-   *          DOCUMENT ME!
-   */
   @Override
   public void paintComponent(Graphics g)
   {
index 35ae242..15adafc 100755 (executable)
@@ -150,6 +150,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
    */
   public AnnotationPanel(AlignmentPanel ap)
   {
+    setName("AnnotationPanel");
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
     ToolTipManager.sharedInstance().setDismissDelay(10000);
@@ -355,7 +356,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         @Override
         public void colourSelected(Color c)
         {
-          HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
+          HiddenColumns hiddenColumns = av.getAlignment()
+                  .getHiddenColumns();
           for (int index : av.getColumnSelection().getSelected())
           {
             if (hiddenColumns.isVisible(index))
@@ -366,10 +368,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
               }
               fAnot[index].colour = c;
             }
-        }};
+          }
+        };
       };
-      JalviewColourChooser.showColourChooser(this,
-              title, Color.black, listener);
+      JalviewColourChooser.showColourChooser(this, title, Color.black,
+              listener);
     }
     else
     // HELIX, SHEET or STEM
@@ -634,6 +637,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     mouseDragLastX = -1;
     mouseDragLastY = -1;
     mouseDragging = false;
+    if (dragMode == DragMode.Resize)
+    {
+      ap.adjustAnnotationHeight();
+    }
     dragMode = DragMode.Undefined;
     ap.getScalePanel().mouseReleased(evt);
 
@@ -674,16 +681,25 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on starting or continuing a mouse drag. There are two possible
+   * actions:
+   * <ul>
+   * <li>drag up or down on a graphed annotation increases or decreases the
+   * height of the graph</li>
+   * <li>dragging left or right selects the columns dragged across</li>
+   * </ul>
+   * A drag on a graph annotation is treated as column selection if it starts
+   * with more horizontal than vertical movement, and as resize if it starts
+   * with more vertical than horizontal movement. Once started, the drag does
+   * not change mode.
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseDragged(MouseEvent evt)
   {
     /*
-     * todo: if dragMode is Undefined:
+     * if dragMode is Undefined:
      * - set to Select if dx > dy
      * - set to Resize if dy > dx
      * - do nothing if dx == dy
@@ -711,14 +727,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     }
 
     if (dragMode == DragMode.Undefined)
-    {
+      {
       /*
        * drag is diagonal - defer deciding whether to
        * treat as up/down or left/right
        */
-      return;
-    }
-
+        return;
+      }
     try
     {
       if (dragMode == DragMode.Resize)
@@ -734,6 +749,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
           int newHeight = Math.max(0, graphAnnotation.graphHeight + deltaY);
           graphAnnotation.graphHeight = newHeight;
           adjustPanelHeight();
+          setNoFastPaint();
           ap.paintAlignment(false, false);
         }
       }
@@ -875,6 +891,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
             && ann.annotations[column] != null)
     {
       tooltip = ann.annotations[column].description;
+      if ("".equals(tooltip))
+      {
+        tooltip = null;
+      }
     }
 
     return tooltip;
@@ -986,7 +1006,9 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   }
 
   private volatile boolean imageFresh = false;
-  private Rectangle visibleRect = new Rectangle(), clipBounds = new Rectangle();
+
+  private Rectangle visibleRect = new Rectangle(),
+          clipBounds = new Rectangle();
 
   /**
    * DOCUMENT ME!
@@ -997,35 +1019,40 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   @Override
   public void paintComponent(Graphics g)
   {
-         
-         // BH: note that this method is generally recommended to 
-         // call super.paintComponent(g). Otherwise, the children of this
-         // component will not be rendered. That is not needed here 
-         // because AnnotationPanel does not have any children. It is
-         // just a JPanel contained in a JViewPort. 
+
+    // BH: note that this method is generally recommended to
+    // call super.paintComponent(g). Otherwise, the children of this
+    // component will not be rendered. That is not needed here
+    // because AnnotationPanel does not have any children. It is
+    // just a JPanel contained in a JViewPort.
 
     computeVisibleRect(visibleRect);
-    
+
     g.setColor(Color.white);
     g.fillRect(0, 0, visibleRect.width, visibleRect.height);
 
-    if (image != null)
+    ViewportRanges ranges = av.getRanges();
+
+    if (allowFastPaint && image != null)
     {
-       // BH 2018 optimizing generation of new Rectangle().
-      if (fastPaint || (visibleRect.width != (clipBounds = g.getClipBounds(clipBounds)).width)
-            || (visibleRect.height != clipBounds.height))
+      // BH 2018 optimizing generation of new Rectangle().
+      if (fastPaint
+              || (visibleRect.width != (clipBounds = g
+                      .getClipBounds(clipBounds)).width)
+              || (visibleRect.height != clipBounds.height))
       {
-
-         
-         g.drawImage(image, 0, 0, this);
+        g.drawImage(image, 0, 0, this);
         fastPaint = false;
         return;
       }
     }
-    imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes()
-            + 1) * av.getCharWidth();
+
+    imgWidth = (ranges.getEndRes() - ranges.getStartRes() + 1)
+            * av.getCharWidth();
+
     if (imgWidth < 1)
     {
+      fastPaint = false;
       return;
     }
     Graphics2D gg;
@@ -1064,24 +1091,20 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       gg.setColor(Color.white);
       gg.fillRect(0, 0, imgWidth, image.getHeight());
       imageFresh = true;
-    } else {
-        gg = (Graphics2D) image.getGraphics();
+    }
+    else
+    {
+      gg = (Graphics2D) image.getGraphics();
 
     }
-    
-    drawComponent(gg, av.getRanges().getStartRes(),
-            av.getRanges().getEndRes() + 1);
+
+    drawComponent(gg, ranges.getStartRes(), av.getRanges().getEndRes() + 1);
     gg.dispose();
     imageFresh = false;
     g.drawImage(image, 0, 0, this);
   }
 
   /**
-   * set true to enable redraw timing debug output on stderr
-   */
-  private final boolean debugRedraw = false;
-
-  /**
    * non-Thread safe repaint
    * 
    * @param horizontal
@@ -1102,6 +1125,12 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     int er = av.getRanges().getEndRes() + 1;
     int transX = 0;
 
+    if (er == sr + 1)
+    {
+      fastPaint = false;
+      return;
+    }
+
     Graphics2D gg = (Graphics2D) image.getGraphics();
 
     gg.copyArea(0, 0, imgWidth, getHeight(),
@@ -1124,7 +1153,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     gg.translate(-transX, 0);
 
     gg.dispose();
-    
+
     fastPaint = true;
 
     // Call repaint on alignment panel so that repaints from other alignment
@@ -1242,6 +1271,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
   private int[] bounds = new int[2];
 
+  private boolean allowFastPaint;
+
   @Override
   public int[] getVisibleVRange()
   {
@@ -1268,7 +1299,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     ap = null;
     image = null;
     fadedImage = null;
-//    gg = null;
+    // gg = null;
     _mwl = null;
 
     /*
@@ -1348,4 +1379,14 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     }
     return annotationHeight;
   }
+  
+  /**
+   * Clears the flag that allows a 'fast paint' on the next repaint, so
+   * requiring a full repaint
+   */
+  public void setNoFastPaint()
+  {
+    allowFastPaint = false;
+  }
+
 }
index e13df4a..afb727f 100644 (file)
@@ -546,7 +546,7 @@ public class AppJmol extends StructureViewerBase
     }
     if (errormsgs.length() > 0)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.pdb_entries_couldnt_be_retrieved", new String[]
                       { errormsgs.toString() }),
index 20e8dfe..5b5bd18 100644 (file)
  */
 package jalview.gui;
 
-import jalview.api.StructureSelectionManagerProvider;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
-import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 
 /**
- * GUI related routines for associating PDB files with sequences
+ * GUI related routines for associating PDB files with sequences. A single
+ * static method.
  * 
  * @author JimP
  * 
@@ -37,58 +36,56 @@ import jalview.util.MessageManager;
 public class AssociatePdbFileWithSeq
 {
 
+  private AssociatePdbFileWithSeq()
+  {
+    // inaccessible
+  }
+
   /**
-   * assocate the given PDB file with
+   * Associate the given PDB file name or URL with a sequence. Do not map
+   * mouse-over events.
    * 
-   * @param choice
+   * @param fileName
+   *          or URL
+   * @param type
+   *          will be DataType.FILE or DataType.URL
    * @param sequence
+   *          to associate
+   * @param prompt
+   *          true if the user should be asked what to do if the specified file
+   *          does not seem to contain PDB information (StructureChooser only)
+   * @return null if file is not found
    */
-  public PDBEntry associatePdbWithSeq(String choice, DataSourceType file,
-          SequenceI sequence, boolean prompt,
-          StructureSelectionManagerProvider ssmp)
+  public static PDBEntry associatePdbWithSeq(String fileName,
+          DataSourceType type, SequenceI sequence, boolean prompt)
   {
     PDBEntry entry = new PDBEntry();
     StructureFile pdbfile = null;
-    pdbfile = StructureSelectionManager.getStructureSelectionManager(ssmp)
+    pdbfile = Desktop.getStructureSelectionManager()
             .setMapping(false, new SequenceI[]
-            { sequence }, null, choice, file);
+            { sequence }, null, fileName, type);
     if (pdbfile == null)
     {
       // stacktrace already thrown so just return
       return null;
     }
-    if (pdbfile.getId() == null)
+    String id = pdbfile.getId();
+    if (id == null && (id = (prompt
+            ? JvOptionPane.showInternalInputDialog(Desktop.getDesktopPane(),
+                    MessageManager
+                            .getString("label.couldnt_find_pdb_id_in_file"),
+                    MessageManager.getString("label.no_pdb_id_in_file"),
+                    JvOptionPane.QUESTION_MESSAGE)
+            : null)) == null)
     {
-      String reply = null;
-
-      if (prompt)
-      {
-        reply = JvOptionPane.showInternalInputDialog(Desktop.desktop,
-                MessageManager
-                        .getString("label.couldnt_find_pdb_id_in_file"),
-                MessageManager.getString("label.no_pdb_id_in_file"),
-                JvOptionPane.QUESTION_MESSAGE);
-      }
-      if (reply == null)
-      {
-        return null;
-      }
-
-      entry.setId(reply);
-    }
-    else
-    {
-      entry.setId(pdbfile.getId());
+      return null;
     }
+    entry.setId(id);
     entry.setType(PDBEntry.Type.FILE);
-
-    if (pdbfile != null)
-    {
-      entry.setFile(choice);
-      sequence.getDatasetSequence().addPDBId(entry);
-      StructureSelectionManager.getStructureSelectionManager(ssmp)
-              .registerPDBEntry(entry);
-    }
+    entry.setFile(fileName);
+    sequence.getDatasetSequence().addPDBId(entry);
+    Desktop.getStructureSelectionManager()
+            .registerPDBEntry(entry);
     return entry;
   }
 }
index 3ad924a..46d02f7 100644 (file)
@@ -62,8 +62,13 @@ import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
 /**
- * A dialog where a user can choose and action Tree or PCA calculation options
+ * A dialog where a user can choose and action Tree or PCA calculation options.
+ * 
+ * Allows also for dialog-free static methods openPCAPanel(...) and
+ * openTreePanel(...) for scripted use.
+ * 
  */
+@SuppressWarnings("serial")
 public class CalculationChooser extends JPanel
 {
   /*
@@ -74,7 +79,7 @@ public class CalculationChooser extends JPanel
    */
   private static boolean treeMatchGaps = true;
 
-  private static final Font VERDANA_11PT = new Font("Verdana", 0, 11);
+  private static Font VERDANA_11PT;
 
   private static final int MIN_TREE_SELECTION = 3;
 
@@ -102,7 +107,7 @@ public class CalculationChooser extends JPanel
 
   private JCheckBox shorterSequence;
 
-  final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
+  private static ComboBoxTooltipRenderer renderer; // BH was not static
 
   List<String> tips = new ArrayList<>();
 
@@ -112,6 +117,37 @@ public class CalculationChooser extends JPanel
   private PCAPanel pcaPanel;
 
   /**
+   * Open a new Tree panel on the desktop statically. Params are standard (not
+   * set by Groovy). No dialog is opened.
+   * 
+   * @param af
+   * @param treeType
+   * @param modelName
+   * @return null if successful; the string
+   *         "label.you_need_at_least_n_sequences" if number of sequences
+   *         selected is inappropriate
+   */
+  public static Object openTreePanel(AlignFrame af, String treeType,
+          String modelName)
+  {
+    return openTreePanel(af, treeType, modelName, null);
+  }
+
+  /**
+   * public static method for JalviewJS API to open a PCAPanel without
+   * necessarily using a dialog.
+   * 
+   * @param af
+   * @param modelName
+   * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences"
+   *         if number of sequences selected is inappropriate
+   */
+  public static Object openPcaPanel(AlignFrame af, String modelName)
+  {
+    return openPcaPanel(af, modelName, null);
+  }
+
+  /**
    * Constructor
    * 
    * @param af
@@ -232,6 +268,10 @@ public class CalculationChooser extends JPanel
     paramsPanel.add(includeGappedColumns);
     paramsPanel.add(shorterSequence);
 
+    if (VERDANA_11PT == null)
+    {
+      VERDANA_11PT = new Font("Verdana", 0, 11);
+    }
     /*
      * OK / Cancel buttons
      */
@@ -279,7 +319,7 @@ public class CalculationChooser extends JPanel
       title = title + " (" + af.getViewport().getViewName() + ")";
     }
 
-    Desktop.addInternalFrame(frame, title, width, height, false);
+    Desktop.addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, width, height, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_SET_MIN_SIZE_300);
     calcChoicePanel.doLayout();
     revalidate();
     /*
@@ -380,7 +420,11 @@ public class CalculationChooser extends JPanel
    */
   protected JComboBox<String> buildModelOptionsList()
   {
-    final JComboBox<String> scoreModelsCombo = new JComboBox<>();
+    JComboBox<String> scoreModelsCombo = new JComboBox<>();
+    if (renderer == null)
+    {
+      renderer = new ComboBoxTooltipRenderer();
+    }
     scoreModelsCombo.setRenderer(renderer);
 
     /*
@@ -500,7 +544,8 @@ public class CalculationChooser extends JPanel
      * for backwards compatibility with Jalview < 2.8 (JAL-2962)
      */
     if (nucleotide && forPca
-            && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false))
+            && Cache.getDefault(Preferences.BLOSUM62_PCA_FOR_NUCLEOTIDE,
+                    false))
     {
       filtered.add(scoreModels.getBlosum62());
     }
@@ -537,6 +582,63 @@ public class CalculationChooser extends JPanel
    */
   protected void openTreePanel(String modelName, SimilarityParamsI params)
   {
+    Object ret = openTreePanel(af,
+            neighbourJoining.isSelected() ? TreeBuilder.NEIGHBOUR_JOINING
+                    : TreeBuilder.AVERAGE_DISTANCE,
+            modelName, params);
+    if (ret instanceof String)
+    {
+      JvOptionPane.showMessageDialog(this, // was opening on Desktop?
+              MessageManager.formatMessage(
+                      (String) ret,
+                      MIN_TREE_SELECTION),
+              MessageManager.getString("label.not_enough_sequences"),
+              JvOptionPane.WARNING_MESSAGE);
+
+    }
+  }
+
+  /**
+   * Open a new PCA panel on the desktop
+   * 
+   * @param modelName
+   * @param params
+   */
+  protected void openPcaPanel(String modelName, SimilarityParamsI params)
+  {
+    Object ret = openPcaPanel(af, modelName, params);
+    if (ret instanceof String)
+    {
+      JvOptionPane.showInternalMessageDialog(this,
+              MessageManager.formatMessage(
+                      (String) ret,
+                      MIN_PCA_SELECTION),
+              MessageManager
+                      .getString("label.sequence_selection_insufficient"),
+              JvOptionPane.WARNING_MESSAGE);
+    }
+    else
+    {
+      // only used for test suite
+      pcaPanel = (PCAPanel) ret;
+    }
+
+  }
+
+  /**
+   * Open a new Tree panel on the desktop statically
+   * 
+   * @param af
+   * @param treeType
+   * @param modelName
+   * @param params
+   * @return null, or the string "label.you_need_at_least_n_sequences" if number
+   *         of sequences selected is inappropriate
+   */
+  public static Object openTreePanel(AlignFrame af, String treeType,
+          String modelName, SimilarityParamsI params)
+  {
+
     /*
      * gui validation shouldn't allow insufficient sequences here, but leave
      * this check in in case this method gets exposed programmatically in future
@@ -545,56 +647,57 @@ public class CalculationChooser extends JPanel
     SequenceGroup sg = viewport.getSelectionGroup();
     if (sg != null && sg.getSize() < MIN_TREE_SELECTION)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
-              MessageManager.formatMessage(
-                      "label.you_need_at_least_n_sequences",
-                      MIN_TREE_SELECTION),
-              MessageManager.getString("label.not_enough_sequences"),
-              JvOptionPane.WARNING_MESSAGE);
-      return;
+      return "label.you_need_at_least_n_sequences";
+    }
+
+    if (params == null)
+    {
+      params = getSimilarityParameters(false);
     }
 
-    String treeType = neighbourJoining.isSelected()
-            ? TreeBuilder.NEIGHBOUR_JOINING
-            : TreeBuilder.AVERAGE_DISTANCE;
     af.newTreePanel(treeType, modelName, params);
+    return null;
   }
 
   /**
-   * Open a new PCA panel on the desktop
+   * public static method for JalviewJS API
    * 
+   * @param af
    * @param modelName
    * @param params
+   * @return the PCAPanel, or null if number of sequences selected is
+   *         inappropriate
    */
-  protected void openPcaPanel(String modelName, SimilarityParamsI params)
+  public static Object openPcaPanel(AlignFrame af, String modelName,
+          SimilarityParamsI params)
   {
     AlignViewportI viewport = af.getViewport();
 
     /*
      * gui validation shouldn't allow insufficient sequences here, but leave
      * this check in in case this method gets exposed programmatically in future
+     * 
+     * 
      */
     if (((viewport.getSelectionGroup() != null)
             && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION)
             && (viewport.getSelectionGroup().getSize() > 0))
             || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION))
     {
-      JvOptionPane.showInternalMessageDialog(this,
-              MessageManager.formatMessage(
-                      "label.you_need_at_least_n_sequences",
-                      MIN_PCA_SELECTION),
-              MessageManager
-                      .getString("label.sequence_selection_insufficient"),
-              JvOptionPane.WARNING_MESSAGE);
-      return;
+      return "label.you_need_at_least_n_sequences";
+    }
+
+    if (params == null)
+    {
+      params = getSimilarityParameters(true);
     }
 
     /*
      * construct the panel and kick off its calculation thread
      */
-    pcaPanel = new PCAPanel(af.alignPanel, modelName, params);
-    new Thread(pcaPanel).start();
-
+    PCAPanel pcap = new PCAPanel(af.alignPanel, modelName, params);
+    new Thread(pcap).start();
+    return pcap;
   }
 
   /**
@@ -610,6 +713,7 @@ public class CalculationChooser extends JPanel
     }
   }
 
+
   /**
    * Returns a data bean holding parameters for similarity (or distance) model
    * calculation
@@ -617,7 +721,8 @@ public class CalculationChooser extends JPanel
    * @param doPCA
    * @return
    */
-  protected SimilarityParamsI getSimilarityParameters(boolean doPCA)
+  public static SimilarityParamsI getSimilarityParameters(
+          boolean doPCA)
   {
     // commented out: parameter choices read from gui widgets
     // SimilarityParamsI params = new SimilarityParams(
@@ -638,6 +743,7 @@ public class CalculationChooser extends JPanel
 
     return new SimilarityParams(includeGapGap, matchGap, includeGapResidue,
             matchOnShortestLength);
+
   }
 
   /**
index c6d6e97..317eff5 100644 (file)
@@ -336,7 +336,7 @@ public class ChimeraViewFrame extends StructureViewerBase
 
     if (!jmb.launchChimera())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.getString("label.chimera_failed"),
               MessageManager.getString("label.error_loading_file"),
               JvOptionPane.ERROR_MESSAGE);
@@ -497,7 +497,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     if (errormsgs.length() > 0)
     {
 
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.pdb_entries_couldnt_be_retrieved", new Object[]
                       { errormsgs.toString() }),
index 6eab07d..e38b55b 100644 (file)
@@ -158,7 +158,7 @@ public class ColourMenuHelper
             ActionListener al = radioItem.getActionListeners()[0];
             radioItem.removeActionListener(al);
             int option = JvOptionPane.showInternalConfirmDialog(
-                    Desktop.desktop,
+                    Desktop.getDesktopPane(),
                     MessageManager
                             .getString("label.remove_from_default_list"),
                     MessageManager
@@ -314,7 +314,7 @@ public class ColourMenuHelper
     }
     else
     {
-      Cache.applicationProperties.remove("USER_DEFINED_COLOURS");
+      Cache.removePropertyNoSave("USER_DEFINED_COLOURS");
     }
   }
 }
index 2ada4d2..cc25696 100644 (file)
@@ -454,7 +454,7 @@ public class CrossRefAction implements Runnable
             .setGapCharacter(alignFrame.viewport.getGapCharacter());
 
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
 
     /*
      * register any new mappings for sequence mouseover etc
index d328a0d..4badcba 100644 (file)
@@ -234,7 +234,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
               .println(MessageManager.getString("label.couldnt_read_data"));
       if (!Jalview.isHeadlessMode())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 AppletFormatAdapter.getSupportedFormats(),
                 MessageManager.getString("label.couldnt_read_data"),
                 JvOptionPane.WARNING_MESSAGE);
@@ -253,7 +253,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
 
     } catch (IOException ex)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), MessageManager
               .formatMessage("label.couldnt_read_pasted_text", new String[]
               { ex.toString() }),
               MessageManager.getString("label.error_parsing_text"),
@@ -342,7 +342,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
               .println(MessageManager.getString("label.couldnt_read_data"));
       if (!Jalview.isHeadlessMode())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 AppletFormatAdapter.getSupportedFormats(),
                 MessageManager.getString("label.couldnt_read_data"),
                 JvOptionPane.WARNING_MESSAGE);
index 572563a..88c94f6 100644 (file)
@@ -97,6 +97,9 @@ import org.stackoverflowusers.file.WindowsShortcut;
 
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.StructureSelectionManagerProvider;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
 import jalview.gui.ImageExporter.ImageWriterI;
@@ -111,6 +114,7 @@ import jalview.io.FormatAdapter;
 import jalview.io.IdentifyFile;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
+import jalview.jbgui.GDesktop;
 import jalview.jbgui.GSplitFrame;
 import jalview.jbgui.GStructureViewer;
 import jalview.project.Jalview2XML;
@@ -120,7 +124,6 @@ import jalview.util.BrowserLauncher;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-import jalview.util.ShortcutKeyMaskExWrapper;
 import jalview.util.UrlConstants;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.WSDiscovererI;
@@ -134,9 +137,11 @@ import jalview.ws.utils.UrlDownloadClient;
  * @author $author$
  * @version $Revision: 1.155 $
  */
-public class Desktop extends jalview.jbgui.GDesktop
+@SuppressWarnings("serial")
+public class Desktop extends GDesktop
         implements DropTargetListener, ClipboardOwner, IProgressIndicator,
-        jalview.api.StructureSelectionManagerProvider
+        StructureSelectionManagerProvider, ApplicationSingletonI
+
 {
   private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
           + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
@@ -209,31 +214,47 @@ public class Desktop extends jalview.jbgui.GDesktop
             listener);
   }
 
-  /** Singleton Desktop instance */
-  public static Desktop instance;
+  private MyDesktopPane desktopPane;
 
-  public static MyDesktopPane desktop;
+  public static MyDesktopPane getDesktopPane()
+  {
+    Desktop desktop = getInstance();
+    return desktop == null ? null : desktop.desktopPane;
+  }
 
-  public static MyDesktopPane getDesktop()
+  /**
+   * Answers an 'application scope' singleton instance of this class. Separate
+   * SwingJS 'applets' running in the same browser page will each have a
+   * distinct instance of Desktop.
+   * 
+   * @return
+   */
+  public static Desktop getInstance()
   {
-    // BH 2018 could use currentThread() here as a reference to a
-    // Hashtable<Thread, MyDesktopPane> in JavaScript
-    return desktop;
+    return Jalview.isHeadlessMode() ? null
+            : (Desktop) ApplicationSingletonProvider
+                    .getInstance(Desktop.class);
   }
 
-  static int openFrameCount = 0;
+  public static StructureSelectionManager getStructureSelectionManager()
+  {
+    return StructureSelectionManager
+            .getStructureSelectionManager(getInstance());
+  }
+
+  int openFrameCount = 0;
 
-  static final int xOffset = 30;
+  final int xOffset = 30;
 
-  static final int yOffset = 30;
+  final int yOffset = 30;
 
-  public static jalview.ws.jws1.Discoverer discoverer;
+  public jalview.ws.jws1.Discoverer discoverer;
 
-  public static Object[] jalviewClipboard;
+  public Object[] jalviewClipboard;
 
-  public static boolean internalCopy = false;
+  public boolean internalCopy = false;
 
-  static int fileLoadingCount = 0;
+  int fileLoadingCount = 0;
 
   class MyDesktopManager implements DesktopManager
   {
@@ -254,7 +275,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       } catch (NullPointerException npe)
       {
         Point p = getMousePosition();
-        instance.showPasteMenu(p.x, p.y);
+        showPasteMenu(p.x, p.y);
       }
     }
 
@@ -302,14 +323,14 @@ public class Desktop extends jalview.jbgui.GDesktop
     public void endDraggingFrame(JComponent f)
     {
       delegate.endDraggingFrame(f);
-      desktop.repaint();
+      desktopPane.repaint();
     }
 
     @Override
     public void endResizingFrame(JComponent f)
     {
       delegate.endResizingFrame(f);
-      desktop.repaint();
+      desktopPane.repaint();
     }
 
     @Override
@@ -359,190 +380,199 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   /**
-   * Creates a new Desktop object.
+   * Private constructor enforces singleton pattern. It is called by reflection
+   * from ApplicationSingletonProvider.getInstance().
    */
-  public Desktop()
+  private Desktop()
   {
-    super();
-    /**
-     * A note to implementors. It is ESSENTIAL that any activities that might
-     * block are spawned off as threads rather than waited for during this
-     * constructor.
-     */
-    instance = this;
-
-    doConfigureStructurePrefs();
-    setTitle("Jalview " + Cache.getProperty("VERSION"));
-    /*
-    if (!Platform.isAMac())
-    {
-      // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
-    }
-    else
-    {
-     this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
-    }
-    */
-
+    Cache.initLogger();
     try
     {
-      APQHandlers.setAPQHandlers(this);
-    } catch (Throwable t)
-    {
-      System.out.println("Error setting APQHandlers: " + t.toString());
-      // t.printStackTrace();
-    }
-
-    addWindowListener(new WindowAdapter()
-    {
+      /**
+       * A note to implementors. It is ESSENTIAL that any activities that might
+       * block are spawned off as threads rather than waited for during this
+       * constructor.
+       */
 
-      @Override
-      public void windowClosing(WindowEvent ev)
+      doConfigureStructurePrefs();
+      setTitle("Jalview " + Cache.getProperty("VERSION"));
+      /*
+      if (!Platform.isAMac())
       {
-        quit();
+      // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
       }
-    });
+      else
+      {
+       this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+      }
+      */
 
-    boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
-            false);
+      try
+      {
+        APQHandlers.setAPQHandlers(this);
+      } catch (Throwable t)
+      {
+        System.out.println("Error setting APQHandlers: " + t.toString());
+        // t.printStackTrace();
+      }
 
-    boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
-            false);
-    desktop = new MyDesktopPane(selmemusage);
+      addWindowListener(new WindowAdapter()
+      {
 
-    showMemusage.setSelected(selmemusage);
-    desktop.setBackground(Color.white);
+        @Override
+        public void windowClosing(WindowEvent ev)
+        {
+          quit();
+        }
+      });
 
-    getContentPane().setLayout(new BorderLayout());
-    // alternate config - have scrollbars - see notes in JAL-153
-    // JScrollPane sp = new JScrollPane();
-    // sp.getViewport().setView(desktop);
-    // getContentPane().add(sp, BorderLayout.CENTER);
+      boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
 
-    // BH 2018 - just an experiment to try unclipped JInternalFrames.
-    if (Platform.isJS())
-    {
-      getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
-    }
+      boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false);
+      desktopPane = new MyDesktopPane(selmemusage);
 
-    getContentPane().add(desktop, BorderLayout.CENTER);
-    desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
+      showMemusage.setSelected(selmemusage);
+      desktopPane.setBackground(Color.white);
 
-    // This line prevents Windows Look&Feel resizing all new windows to maximum
-    // if previous window was maximised
-    desktop.setDesktopManager(new MyDesktopManager(
-            (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
-                    : Platform.isAMacAndNotJS()
-                            ? new AquaInternalFrameManager(
-                                    desktop.getDesktopManager())
-                            : desktop.getDesktopManager())));
+      getContentPane().setLayout(new BorderLayout());
+      // alternate config - have scrollbars - see notes in JAL-153
+      // JScrollPane sp = new JScrollPane();
+      // sp.getViewport().setView(desktop);
+      // getContentPane().add(sp, BorderLayout.CENTER);
 
-    Rectangle dims = getLastKnownDimensions("");
-    if (dims != null)
-    {
-      setBounds(dims);
-    }
-    else
-    {
-      Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-      int xPos = Math.max(5, (screenSize.width - 900) / 2);
-      int yPos = Math.max(5, (screenSize.height - 650) / 2);
-      setBounds(xPos, yPos, 900, 650);
-    }
+      // BH 2018 - just an experiment to try unclipped JInternalFrames.
+      if (Platform.isJS())
+      {
+        getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
+      }
 
-    if (!Platform.isJS())
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
-    {
-      jconsole = new Console(this, showjconsole);
-      jconsole.setHeader(Cache.getVersionDetailsForConsole());
-      showConsole(showjconsole);
+      getContentPane().add(desktopPane, BorderLayout.CENTER);
+      desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
 
-      showNews.setVisible(false);
+      // This line prevents Windows Look&Feel resizing all new windows to
+      // maximum
+      // if previous window was maximised
+      desktopPane.setDesktopManager(new MyDesktopManager(
+              (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
+                      : Platform.isAMacAndNotJS()
+                              ? new AquaInternalFrameManager(
+                                      desktopPane.getDesktopManager())
+                              : desktopPane.getDesktopManager())));
 
-      experimentalFeatures.setSelected(showExperimental());
+      Rectangle dims = getLastKnownDimensions("");
+      if (dims != null)
+      {
+        setBounds(dims);
+      }
+      else
+      {
+        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+        int xPos = Math.max(5, (screenSize.width - 900) / 2);
+        int yPos = Math.max(5, (screenSize.height - 650) / 2);
+        setBounds(xPos, yPos, 900, 650);
+      }
 
       getIdentifiersOrgData();
 
-      checkURLLinks();
+      if (!Platform.isJS())
+      /**
+       * Java only
+       * 
+       * @j2sIgnore
+       */
+      {
+        jconsole = new Console(this, showjconsole);
+        jconsole.setHeader(Cache.getVersionDetailsForConsole());
+        showConsole(showjconsole);
 
-      // Spawn a thread that shows the splashscreen
+        showNews.setVisible(false);
 
-      SwingUtilities.invokeLater(new Runnable()
-      {
-        @Override
-        public void run()
-        {
-          new SplashScreen(true);
-        }
-      });
+        experimentalFeatures.setSelected(showExperimental());
 
-      // Thread off a new instance of the file chooser - this reduces the time
-      // it
-      // takes to open it later on.
-      new Thread(new Runnable()
-      {
-        @Override
-        public void run()
+        if (Jalview.isInteractive())
         {
-          Cache.log.debug("Filechooser init thread started.");
-          String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
-          JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
-                  fileFormat);
-          Cache.log.debug("Filechooser init thread finished.");
-        }
-      }).start();
-      // Add the service change listener
-      changeSupport.addJalviewPropertyChangeListener("services",
-              new PropertyChangeListener()
-              {
+          // disabled for SeqCanvasTest
+          checkURLLinks();
 
-                @Override
-                public void propertyChange(PropertyChangeEvent evt)
-                {
-                  Cache.log.debug("Firing service changed event for "
-                          + evt.getNewValue());
-                  JalviewServicesChanged(evt);
-                }
-              });
-    }
+          // Spawn a thread that shows the splashscreen
 
-    this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
+          SwingUtilities.invokeLater(new Runnable()
+          {
+            @Override
+            public void run()
+            {
+              new SplashScreen(true);
+            }
+          });
 
-    this.addWindowListener(new WindowAdapter()
-    {
-      @Override
-      public void windowClosing(WindowEvent evt)
-      {
-        quit();
+          // Thread off a new instance of the file chooser - this reduces the
+          // time
+          // it
+          // takes to open it later on.
+          new Thread(new Runnable()
+          {
+            @Override
+            public void run()
+            {
+              Cache.log.debug("Filechooser init thread started.");
+              String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
+              JalviewFileChooser.forRead(
+                      Cache.getProperty("LAST_DIRECTORY"), fileFormat);
+              Cache.log.debug("Filechooser init thread finished.");
+            }
+          }).start();
+          // Add the service change listener
+          changeSupport.addJalviewPropertyChangeListener("services",
+                  new PropertyChangeListener()
+                  {
+
+                    @Override
+                    public void propertyChange(PropertyChangeEvent evt)
+                    {
+                      Cache.log.debug("Firing service changed event for "
+                              + evt.getNewValue());
+                      JalviewServicesChanged(evt);
+                    }
+                  });
+        }
       }
-    });
+      this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
 
-    MouseAdapter ma;
-    this.addMouseListener(ma = new MouseAdapter()
-    {
-      @Override
-      public void mousePressed(MouseEvent evt)
+      this.addWindowListener(new WindowAdapter()
       {
-        if (evt.isPopupTrigger()) // Mac
+        @Override
+        public void windowClosing(WindowEvent evt)
         {
-          showPasteMenu(evt.getX(), evt.getY());
+          quit();
         }
-      }
+      });
 
-      @Override
-      public void mouseReleased(MouseEvent evt)
+      MouseAdapter ma;
+      this.addMouseListener(ma = new MouseAdapter()
       {
-        if (evt.isPopupTrigger()) // Windows
+        @Override
+        public void mousePressed(MouseEvent evt)
         {
-          showPasteMenu(evt.getX(), evt.getY());
+          if (evt.isPopupTrigger()) // Mac
+          {
+            showPasteMenu(evt.getX(), evt.getY());
+          }
         }
-      }
-    });
-    desktop.addMouseListener(ma);
+
+        @Override
+        public void mouseReleased(MouseEvent evt)
+        {
+          if (evt.isPopupTrigger()) // Windows
+          {
+            showPasteMenu(evt.getX(), evt.getY());
+          }
+        }
+      });
+      desktopPane.addMouseListener(ma);
+    } catch (Throwable t)
+    {
+      t.printStackTrace();
+    }
 
   }
 
@@ -566,10 +596,10 @@ public class Desktop extends jalview.jbgui.GDesktop
             .getStructureSelectionManager(this);
     if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
     {
-      ssm.setAddTempFacAnnot(Cache
-              .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
-      ssm.setProcessSecondaryStructure(Cache
-              .getDefault(Preferences.STRUCT_FROM_PDB, true));
+      ssm.setAddTempFacAnnot(
+              Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
+      ssm.setProcessSecondaryStructure(
+              Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
       ssm.setSecStructServices(
               Cache.getDefault(Preferences.USE_RNAVIEW, true));
     }
@@ -618,7 +648,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         }
       }
     }).start();
-    
+
   }
 
   @Override
@@ -639,10 +669,10 @@ public class Desktop extends jalview.jbgui.GDesktop
         public void run()
         {
           long now = System.currentTimeMillis();
-          Desktop.instance.setProgressBar(
-                  MessageManager.getString("status.refreshing_news"), now);
+          setProgressBar(MessageManager.getString("status.refreshing_news"),
+                  now);
           jvnews.refreshNews();
-          Desktop.instance.setProgressBar(null, now);
+          setProgressBar(null, now);
           jvnews.showNews();
         }
       }).start();
@@ -663,10 +693,8 @@ public class Desktop extends jalview.jbgui.GDesktop
     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
     String x = Cache.getProperty(windowName + "SCREEN_X");
     String y = Cache.getProperty(windowName + "SCREEN_Y");
-    String width = Cache
-            .getProperty(windowName + "SCREEN_WIDTH");
-    String height = Cache
-            .getProperty(windowName + "SCREEN_HEIGHT");
+    String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
+    String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
     if ((x != null) && (y != null) && (width != null) && (height != null))
     {
       int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
@@ -676,10 +704,10 @@ public class Desktop extends jalview.jbgui.GDesktop
         // attempt #1 - try to cope with change in screen geometry - this
         // version doesn't preserve original jv aspect ratio.
         // take ratio of current screen size vs original screen size.
-        double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
-                Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
-        double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
-                Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
+        double sw = ((1f * screenSize.width) / (1f * Integer
+                .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
+        double sh = ((1f * screenSize.height) / (1f * Integer
+                .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
         // rescale the bounds depending upon the current screen geometry.
         ix = (int) (ix * sw);
         iw = (int) (iw * sw);
@@ -750,48 +778,60 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
   }
 
-  /**
-   * Adds and opens the given frame to the desktop
-   * 
-   * @param frame
-   *          Frame to show
-   * @param title
-   *          Visible Title
-   * @param w
-   *          width
-   * @param h
-   *          height
-   */
-  public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, int w, int h)
-  {
-    addInternalFrame(frame, title, true, w, h, true, false);
-  }
-
-  /**
-   * Add an internal frame to the Jalview desktop
-   * 
-   * @param frame
-   *          Frame to show
-   * @param title
-   *          Visible Title
-   * @param makeVisible
-   *          When true, display frame immediately, otherwise, caller must call
-   *          setVisible themselves.
-   * @param w
-   *          width
-   * @param h
-   *          height
-   */
-  public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, boolean makeVisible,
-          int w, int h)
-  {
-    addInternalFrame(frame, title, makeVisible, w, h, true, false);
-  }
+//  /**
+//   * Add an internal frame to the Jalview desktop that is allowed to be resized,
+//   * has a minimum size of 300px and might or might not be visible
+//   * 
+//   * @param frame
+//   *          Frame to show
+//   * @param title
+//   *          Visible Title
+//   * @param makeVisible
+//   *          When true, display frame immediately, otherwise, caller must call
+//   *          setVisible themselves.
+//   * @param w
+//   *          width
+//   * @param h
+//   *          height
+//   */
+//  @Deprecated
+//  public static synchronized void addInternalFrame(
+//          final JInternalFrame frame, String title, boolean makeVisible,
+//          int w, int h)
+//  {
+//    // textbox, web services, sequenceFetcher, featureSettings
+//    getInstance().addFrame(frame, title, makeVisible, w, h,
+//            FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
+//  }
+//
+//  /**
+//   * Add an internal frame to the Jalview desktop that is visible, has a minimum
+//   * size of 300px, and may or may not be resizable
+//   * 
+//   * @param frame
+//   *          Frame to show
+//   * @param title
+//   *          Visible Title
+//   * @param w
+//   *          width
+//   * @param h
+//   *          height
+//   * @param resizable
+//   *          Allow resize
+//   */
+//  @Deprecated
+//  public static synchronized void addInternalFrame(
+//          final JInternalFrame frame, String title, int w, int h,
+//          boolean resizable)
+//  {
+//    // annotation, font, calculation, user-defined colors
+//    getInstance().addFrame(frame, title, FRAME_MAKE_VISIBLE, w, h,
+//            resizable, FRAME_SET_MIN_SIZE_300);
+//  }
 
   /**
-   * Add an internal frame to the Jalview desktop and make it visible
+   * Adds and opens the given frame to the desktop that is visible, allowed to
+   * resize, and has a 300px minimum width.
    * 
    * @param frame
    *          Frame to show
@@ -801,18 +841,19 @@ public class Desktop extends jalview.jbgui.GDesktop
    *          width
    * @param h
    *          height
-   * @param resizable
-   *          Allow resize
    */
   public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, int w, int h,
-          boolean resizable)
+          final JInternalFrame frame, String title, int w, int h)
   {
-    addInternalFrame(frame, title, true, w, h, resizable, false);
+    // 58 classes
+    
+    addInternalFrame(frame, title, Desktop.FRAME_MAKE_VISIBLE, w, h, 
+            FRAME_ALLOW_RESIZE, FRAME_SET_MIN_SIZE_300);
   }
 
   /**
-   * Add an internal frame to the Jalview desktop
+   * Add an internal frame to the Jalview desktop that may optionally be
+   * visible, resizable, and allowed to be any size
    * 
    * @param frame
    *          Frame to show
@@ -834,7 +875,8 @@ public class Desktop extends jalview.jbgui.GDesktop
           final JInternalFrame frame, String title, boolean makeVisible,
           int w, int h, boolean resizable, boolean ignoreMinSize)
   {
-
+    // 15 classes call this method directly.
+    
     // TODO: allow callers to determine X and Y position of frame (eg. via
     // bounds object).
     // TODO: consider fixing method to update entries in the window submenu with
@@ -845,23 +887,39 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       frame.setSize(w, h);
     }
-    // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
-    // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
-    // IF JALVIEW IS RUNNING HEADLESS
-    // ///////////////////////////////////////////////
-    if (instance == null || (System.getProperty("java.awt.headless") != null
-            && System.getProperty("java.awt.headless").equals("true")))
-    {
-      return;
-    }
+    if (getInstance() != null)
+      getInstance().addFrame(frame, makeVisible, resizable,
+            ignoreMinSize);
+  }
 
-    openFrameCount++;
+  // These can now by put into a single int flag, if desired:
+  
+  public final static boolean FRAME_MAKE_VISIBLE = true;
+
+  public final static boolean FRAME_NOT_VISIBLE = false;
+
+  public final static boolean FRAME_ALLOW_RESIZE = true;
+
+  public final static boolean FRAME_NOT_RESIZABLE = false;
+
+  public final static boolean FRAME_ALLOW_ANY_SIZE = true;
 
+  public final static boolean FRAME_SET_MIN_SIZE_300 = false;
+  
+  private void addFrame(JInternalFrame frame,
+          boolean makeVisible, boolean resizable,
+          boolean ignoreMinSize)
+  {
+
+    openFrameCount++;
+    
+    boolean isEmbedded = (Platform.getEmbeddedAttribute(frame, "id") != null);
+    boolean hasEmbeddedSize = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
+    // Web page embedding allows us to ignore minimum size
+    ignoreMinSize |= hasEmbeddedSize;
+    
     if (!ignoreMinSize)
     {
-      frame.setMinimumSize(
-              new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
-
       // Set default dimension for Alignment Frame window.
       // The Alignment Frame window could be added from a number of places,
       // hence,
@@ -870,6 +928,10 @@ public class Desktop extends jalview.jbgui.GDesktop
       {
         frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
                 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
+      } else {
+        frame.setMinimumSize(
+                new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
+        
       }
     }
 
@@ -879,8 +941,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     frame.setMaximizable(resizable);
     frame.setIconifiable(resizable);
     frame.setOpaque(Platform.isJS());
-
-    if (frame.getX() < 1 && frame.getY() < 1)
+    if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
     {
       frame.setLocation(xOffset * openFrameCount,
               yOffset * ((openFrameCount - 1) % 10) + yOffset);
@@ -890,13 +951,13 @@ public class Desktop extends jalview.jbgui.GDesktop
      * add an entry for the new frame in the Window menu 
      * (and remove it when the frame is closed)
      */
-    final JMenuItem menuItem = new JMenuItem(title);
+    final JMenuItem menuItem = new JMenuItem(frame.getTitle());
     frame.addInternalFrameListener(new InternalFrameAdapter()
     {
       @Override
       public void internalFrameActivated(InternalFrameEvent evt)
       {
-        JInternalFrame itf = desktop.getSelectedFrame();
+        JInternalFrame itf = getDesktopPane().getSelectedFrame();
         if (itf != null)
         {
           if (itf instanceof AlignFrame)
@@ -928,7 +989,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         {
           menuItem.removeActionListener(menuItem.getActionListeners()[0]);
         }
-        windowMenu.remove(menuItem);
+        getInstance().windowMenu.remove(menuItem);
       }
     });
 
@@ -950,9 +1011,9 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     setKeyBindings(frame);
 
-    desktop.add(frame);
+    getDesktopPane().add(frame);
 
-    windowMenu.add(menuItem);
+    getInstance().windowMenu.add(menuItem);
 
     frame.toFront();
     try
@@ -970,14 +1031,13 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   /**
-   * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
-   * window
+   * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
+   * the window
    * 
    * @param frame
    */
   private static void setKeyBindings(JInternalFrame frame)
   {
-    @SuppressWarnings("serial")
     final Action closeAction = new AbstractAction()
     {
       @Override
@@ -993,7 +1053,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
             InputEvent.CTRL_DOWN_MASK);
     KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
+            Platform.SHORTCUT_KEY_MASK);
 
     InputMap inputMap = frame
             .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
@@ -1010,7 +1070,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     if (!internalCopy)
     {
-      Desktop.jalviewClipboard = null;
+      jalviewClipboard = null;
     }
 
     internalCopy = false;
@@ -1055,7 +1115,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     try
     {
-      Desktop.transferFromDropTarget(files, protocols, evt, t);
+      transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -1111,8 +1171,9 @@ public class Desktop extends jalview.jbgui.GDesktop
   public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
   {
     String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
-    JalviewFileChooser chooser = JalviewFileChooser
-            .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
+    JalviewFileChooser chooser = JalviewFileChooser.forRead(
+            Cache.getProperty("LAST_DIRECTORY"), fileFormat,
+            BackupFiles.getEnabled());
 
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
@@ -1245,7 +1306,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           {
             String msg = MessageManager
                     .formatMessage("label.couldnt_locate", url);
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
+            JvOptionPane.showInternalMessageDialog(getDesktopPane(), msg,
                     MessageManager.getString("label.url_not_found"),
                     JvOptionPane.WARNING_MESSAGE);
 
@@ -1266,7 +1327,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     };
     String dialogOption = MessageManager
             .getString("label.input_alignment_from_url");
-    JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
+    JvOptionPane.newOptionDialog(desktopPane).setResponseHandler(0, action)
             .showInternalDialog(panel, dialogOption,
                     JvOptionPane.YES_NO_CANCEL_OPTION,
                     JvOptionPane.PLAIN_MESSAGE, null, options,
@@ -1286,9 +1347,10 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(viewPanel);
-    Desktop.addInternalFrame(cap,
-            MessageManager.getString("label.cut_paste_alignmen_file"), true,
-            600, 500);
+    addInternalFrame(cap,
+            MessageManager.getString("label.cut_paste_alignmen_file"),
+            FRAME_MAKE_VISIBLE, 600, 500, FRAME_ALLOW_RESIZE,
+            FRAME_SET_MIN_SIZE_300);
   }
 
   /*
@@ -1298,10 +1360,8 @@ public class Desktop extends jalview.jbgui.GDesktop
   public void quit()
   {
     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
-    Cache.setProperty("SCREENGEOMETRY_WIDTH",
-            screen.width + "");
-    Cache.setProperty("SCREENGEOMETRY_HEIGHT",
-            screen.height + "");
+    Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
+    Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
     storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
             getWidth(), getHeight()));
 
@@ -1332,9 +1392,9 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   private void storeLastKnownDimensions(String string, Rectangle jc)
   {
-    Cache.log.debug("Storing last known dimensions for "
-            + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
-            + " height:" + jc.height);
+    Cache.log.debug("Storing last known dimensions for " + string + ": x:"
+            + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
+            + jc.height);
 
     Cache.setProperty(string + "SCREEN_X", jc.x + "");
     Cache.setProperty(string + "SCREEN_Y", jc.y + "");
@@ -1445,7 +1505,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   public void closeAll_actionPerformed(ActionEvent e)
   {
     // TODO show a progress bar while closing?
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     for (int i = 0; i < frames.length; i++)
     {
       try
@@ -1512,7 +1572,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   @Override
   protected void showMemusage_actionPerformed(ActionEvent e)
   {
-    desktop.showMemoryUsage(showMemusage.isSelected());
+    desktopPane.showMemoryUsage(showMemusage.isSelected());
   }
 
   /*
@@ -1549,7 +1609,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   void reorderAssociatedWindows(boolean minimize, boolean close)
   {
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     if (frames == null || frames.length < 1)
     {
       return;
@@ -1690,16 +1750,17 @@ public class Desktop extends jalview.jbgui.GDesktop
           setProgressBar(MessageManager.formatMessage(
                   "label.saving_jalview_project", new Object[]
                   { chosenFile.getName() }), chosenFile.hashCode());
-          Cache.setProperty("LAST_DIRECTORY",
-                  chosenFile.getParent());
+          Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
           // TODO catch and handle errors for savestate
           // TODO prevent user from messing with the Desktop whilst we're saving
           try
           {
-               boolean doBackup = BackupFiles.getEnabled();
-            BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
+            boolean doBackup = BackupFiles.getEnabled();
+            BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
+                    : null;
 
-            new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
+            new Jalview2XML().saveState(
+                    doBackup ? backupfiles.getTempFile() : chosenFile);
 
             if (doBackup)
             {
@@ -1725,7 +1786,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           setProgressBar(null, chosenFile.hashCode());
         }
       }).start();
-      }
+    }
   }
 
   @Override
@@ -1756,8 +1817,10 @@ public class Desktop extends jalview.jbgui.GDesktop
         "Jalview Project (old)" };
     JalviewFileChooser chooser = new JalviewFileChooser(
             Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
-            "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
-                                            // allowBackupFiles
+            "Jalview Project", true, BackupFiles.getEnabled()); // last two
+                                                                // booleans:
+                                                                // allFiles,
+    // allowBackupFiles
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
     chooser.setResponseHandler(0, new Runnable()
@@ -1774,29 +1837,30 @@ public class Desktop extends jalview.jbgui.GDesktop
           @Override
           public void run()
           {
-               try 
+            try
             {
               new Jalview2XML().loadJalviewAlign(selectedFile);
             } catch (OutOfMemoryError oom)
-               {
-                 new OOMWarning("Whilst loading project from " + choice, oom);
-               } catch (Exception ex)
-               {
-                 Cache.log.error(
-                         "Problems whilst loading project from " + choice, ex);
-                 JvOptionPane.showMessageDialog(Desktop.desktop,
-                         MessageManager.formatMessage(
-                                 "label.error_whilst_loading_project_from",
-                               new Object[]
-                                   { choice }),
-                         MessageManager.getString("label.couldnt_load_project"),
-                         JvOptionPane.WARNING_MESSAGE);
-               }
+            {
+              new OOMWarning("Whilst loading project from " + choice, oom);
+            } catch (Exception ex)
+            {
+              Cache.log.error(
+                      "Problems whilst loading project from " + choice, ex);
+              JvOptionPane.showMessageDialog(getDesktopPane(),
+                      MessageManager.formatMessage(
+                              "label.error_whilst_loading_project_from",
+                              new Object[]
+                              { choice }),
+                      MessageManager
+                              .getString("label.couldnt_load_project"),
+                      JvOptionPane.WARNING_MESSAGE);
+            }
           }
         }).start();
       }
     });
-    
+
     chooser.showOpenDialog(this);
   }
 
@@ -1827,7 +1891,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       progressPanel = new JPanel(new GridLayout(1, 1));
       totalProgressCount = 0;
-      instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
+      getContentPane().add(progressPanel, BorderLayout.SOUTH);
     }
     JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
     JProgressBar progressBar = new JProgressBar();
@@ -1840,7 +1904,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     ((GridLayout) progressPanel.getLayout()).setRows(
             ((GridLayout) progressPanel.getLayout()).getRows() + 1);
     ++totalProgressCount;
-    instance.validate();
+    validate();
     return thisprogress;
   }
 
@@ -1894,7 +1958,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
   {
-    if (Desktop.desktop == null)
+    if (getDesktopPane() == null)
     {
       // no frames created and in headless mode
       // TODO: verify that frames are recoverable when in headless mode
@@ -1936,9 +2000,9 @@ public class Desktop extends jalview.jbgui.GDesktop
   public static AlignmentViewport[] getViewports(String sequenceSetId)
   {
     List<AlignmentViewport> viewp = new ArrayList<>();
-    if (desktop != null)
+    if (getDesktopPane() != null)
     {
-      AlignFrame[] frames = Desktop.getAlignFrames();
+      AlignFrame[] frames = getAlignFrames();
 
       for (AlignFrame afr : frames)
       {
@@ -1985,9 +2049,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     // FIXME: ideally should use UI interface API
     FeatureSettings viewFeatureSettings = (af.featureSettings != null
-            && af.featureSettings.isOpen())
-            ? af.featureSettings
-            : null;
+            && af.featureSettings.isOpen()) ? af.featureSettings : null;
     Rectangle fsBounds = af.getFeatureSettingsGeometry();
     for (int i = 0; i < size; i++)
     {
@@ -2020,7 +2082,8 @@ public class Desktop extends jalview.jbgui.GDesktop
 
       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
               AlignFrame.DEFAULT_HEIGHT);
-      // and materialise a new feature settings dialog instance for the new alignframe
+      // and materialise a new feature settings dialog instance for the new
+      // alignframe
       // (closes the old as if 'OK' was pressed)
       if (ap == af.alignPanel && newaf.featureSettings != null
               && newaf.featureSettings.isOpen()
@@ -2038,9 +2101,9 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   /**
    * Gather expanded views (separate AlignFrame's) with the same sequence set
-   * identifier back in to this frame as additional views, and close the expanded
-   * views. Note the expanded frames may themselves have multiple views. We take
-   * the lot.
+   * identifier back in to this frame as additional views, and close the
+   * expanded views. Note the expanded frames may themselves have multiple
+   * views. We take the lot.
    * 
    * @param source
    */
@@ -2048,7 +2111,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     source.viewport.setGatherViewsHere(true);
     source.viewport.setExplodedGeometry(source.getBounds());
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     String viewId = source.viewport.getSequenceSetId();
     for (int t = 0; t < frames.length; t++)
     {
@@ -2092,8 +2155,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
 
     // refresh the feature setting UI for the source frame if it exists
-    if (source.featureSettings != null
-            && source.featureSettings.isOpen())
+    if (source.featureSettings != null && source.featureSettings.isOpen())
     {
       source.showFeatureSettingsUI();
     }
@@ -2101,7 +2163,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public JInternalFrame[] getAllFrames()
   {
-    return desktop.getAllFrames();
+    return desktopPane.getAllFrames();
   }
 
   /**
@@ -2187,7 +2249,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           });
           msgPanel.add(jcb);
 
-          JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
+          JvOptionPane.showMessageDialog(desktopPane, msgPanel,
                   MessageManager
                           .getString("label.SEQUENCE_ID_no_longer_used"),
                   JvOptionPane.WARNING_MESSAGE);
@@ -2198,12 +2260,12 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   /**
    * Proxy class for JDesktopPane which optionally displays the current memory
-   * usage and highlights the desktop area with a red bar if free memory runs low.
+   * usage and highlights the desktop area with a red bar if free memory runs
+   * low.
    * 
    * @author AMW
    */
-  public class MyDesktopPane extends JDesktopPane
-          implements Runnable
+  public class MyDesktopPane extends JDesktopPane implements Runnable
   {
     private static final float ONE_MB = 1048576f;
 
@@ -2302,11 +2364,10 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     if (Jalview.isHeadlessMode())
     {
-      // Desktop.desktop is null in headless mode
-      return new AlignFrame[] { Jalview.currentAlignFrame };
+      return new AlignFrame[] { Jalview.getInstance().currentAlignFrame };
     }
 
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = getDesktopPane().getAllFrames();
 
     if (frames == null)
     {
@@ -2351,7 +2412,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public GStructureViewer[] getJmols()
   {
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
 
     if (frames == null)
     {
@@ -2387,7 +2448,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     } catch (Exception ex)
     {
       Cache.log.error("Groovy Shell Creation failed.", ex);
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(desktopPane,
 
               MessageManager.getString("label.couldnt_create_groovy_shell"),
               MessageManager.getString("label.groovy_support_failed"),
@@ -2440,14 +2501,15 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   /**
-   * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
-   * when opened
+   * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
+   * binding when opened
    */
   protected void addQuitHandler()
   {
+
     getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
-                    jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
+                    Platform.SHORTCUT_KEY_MASK),
                     "Quit");
     getRootPane().getActionMap().put("Quit", new AbstractAction()
     {
@@ -2583,15 +2645,16 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   /**
-   * This will return the first AlignFrame holding the given viewport instance. It
-   * will break if there are more than one AlignFrames viewing a particular av.
+   * This will return the first AlignFrame holding the given viewport instance.
+   * It will break if there are more than one AlignFrames viewing a particular
+   * av.
    * 
    * @param viewport
    * @return alignFrame for viewport
    */
   public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
   {
-    if (desktop != null)
+    if (getDesktopPane() != null)
     {
       AlignmentPanel[] aps = getAlignmentPanels(
               viewport.getSequenceSetId());
@@ -2656,7 +2719,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       // todo: changesupport handlers need to be transferred
       if (discoverer == null)
       {
-        discoverer = new jalview.ws.jws1.Discoverer();
+        discoverer = jalview.ws.jws1.Discoverer.getInstance();
         // register PCS handler for desktop.
         discoverer.addPropertyChangeListener(changeSupport);
       }
@@ -2669,7 +2732,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
     {
-      tasks.add(jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer());
+      tasks.add(jalview.ws.jws2.Jws2Discoverer.getInstance().startDiscoverer());
     }
     if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
     {
@@ -2700,8 +2763,9 @@ public class Desktop extends jalview.jbgui.GDesktop
     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
     {
       final WSDiscovererI discoverer = jalview.ws.jws2.Jws2Discoverer
-          .getDiscoverer();
+          .getInstance();
       final String ermsg = discoverer.getErrorMessages();
+      // CONFLICT:ALT:?     final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
       if (ermsg != null)
       {
         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
@@ -2739,7 +2803,7 @@ public class Desktop extends jalview.jbgui.GDesktop
                  * 
                  * jd.waitForInput();
                  */
-                JvOptionPane.showConfirmDialog(Desktop.desktop,
+                JvOptionPane.showConfirmDialog(desktopPane,
                         new JLabel("<html><table width=\"450\"><tr><td>"
                                 + ermsg + "</td></tr></table>"
                                 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
@@ -2776,7 +2840,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    */
   public static void showUrl(final String url)
   {
-    showUrl(url, Desktop.instance);
+    showUrl(url, getInstance());
   }
 
   /**
@@ -2805,7 +2869,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           jalview.util.BrowserLauncher.openURL(url);
         } catch (Exception ex)
         {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+          JvOptionPane.showInternalMessageDialog(getDesktopPane(),
                   MessageManager
                           .getString("label.web_browser_not_found_unix"),
                   MessageManager.getString("label.web_browser_not_found"),
@@ -2845,7 +2909,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       try
       {
         url = e.getURL().toString();
-        Desktop.showUrl(url);
+        showUrl(url);
       } catch (Exception x)
       {
         if (url != null)
@@ -2904,7 +2968,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           {
           }
         }
-        if (instance == null)
+        if (Jalview.isHeadlessMode())
         {
           return;
         }
@@ -2964,8 +3028,8 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   /**
    * Explode the views in the given SplitFrame into separate SplitFrame windows.
-   * This respects (remembers) any previous 'exploded geometry' i.e. the size and
-   * location last time the view was expanded (if any). However it does not
+   * This respects (remembers) any previous 'exploded geometry' i.e. the size
+   * and location last time the view was expanded (if any). However it does not
    * remember the split pane divider location - this is set to match the
    * 'exploding' frame.
    * 
@@ -3030,7 +3094,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       {
         splitFrame.setLocation(geometry.getLocation());
       }
-      Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
+      addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
     }
 
     /*
@@ -3068,7 +3132,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     String topViewId = myTopFrame.viewport.getSequenceSetId();
     String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
 
-    JInternalFrame[] frames = desktop.getAllFrames();
+    JInternalFrame[] frames = desktopPane.getAllFrames();
     for (JInternalFrame frame : frames)
     {
       if (frame instanceof SplitFrame && frame != source)
@@ -3131,12 +3195,14 @@ public class Desktop extends jalview.jbgui.GDesktop
    *          - the payload from the drop event
    * @throws Exception
    */
+  @SuppressWarnings("unchecked")
   public static void transferFromDropTarget(List<Object> files,
           List<DataSourceType> protocols, DropTargetDropEvent evt,
           Transferable t) throws Exception
   {
 
-    // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
+    // BH 2018 changed List<String> to List<Object> to allow for File from
+    // SwingJS
 
     // DataFlavor[] flavors = t.getTransferDataFlavors();
     // for (int i = 0; i < flavors.length; i++) {
@@ -3148,7 +3214,8 @@ public class Desktop extends jalview.jbgui.GDesktop
     // byte[] data = getDroppedFileBytes(file);
     // fileName.setText(file.getName() + " - " + data.length + " " +
     // evt.getLocation());
-    // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
+    // JTextArea target = (JTextArea) ((DropTarget)
+    // evt.getSource()).getComponent();
     // target.setText(new String(data));
     // }
     // dtde.dropComplete(true);
@@ -3200,7 +3267,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       // Works on Windows and MacOSX
       Cache.log.debug("Drop handled as javaFileListFlavor");
-      for (Object file : (List) t
+      for (File file : (List<File>) t
               .getTransferData(DataFlavor.javaFileListFlavor))
       {
         files.add(file);
@@ -3348,10 +3415,10 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   /**
-   * Answers a (possibly empty) list of any structure viewer frames (currently for
-   * either Jmol or Chimera) which are currently open. This may optionally be
-   * restricted to viewers of a specified class, or viewers linked to a specified
-   * alignment panel.
+   * Answers a (possibly empty) list of any structure viewer frames (currently
+   * for either Jmol or Chimera) which are currently open. This may optionally
+   * be restricted to viewers of a specified class, or viewers linked to a
+   * specified alignment panel.
    * 
    * @param apanel
    *          if not null, only return viewers linked to this panel
@@ -3364,7 +3431,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           Class<? extends StructureViewerBase> structureViewerClass)
   {
     List<StructureViewerBase> result = new ArrayList<>();
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
+    JInternalFrame[] frames = getAllFrames();
 
     for (JInternalFrame frame : frames)
     {
@@ -3383,4 +3450,5 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
     return result;
   }
+
 }
index d547c58..a02ec36 100644 (file)
@@ -222,7 +222,7 @@ public class FeatureEditor
               updateColourButton(mainPanel, colour, featureColour);
             };
           };
-          JalviewColourChooser.showColourChooser(Desktop.getDesktop(),
+          JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(),
                   title, featureColour.getColour(), listener);
         }
         else
@@ -412,7 +412,7 @@ public class FeatureEditor
      * set dialog action handlers for OK (create/Amend) and Cancel options
      * also for Delete if applicable (when amending features)
      */
-    JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop)
+    JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane())
             .setResponseHandler(0, okAction).setResponseHandler(2, cancelAction);
     if (!forCreate)
     {
index b49593a..8c4b355 100644 (file)
@@ -436,8 +436,8 @@ public class FeatureSettings extends JPanel
       }
       else
       {
-        Desktop.addInternalFrame(frame, title, false, bounds.width,
-                bounds.height);
+        Desktop.addInternalFrame(frame, title, Desktop.FRAME_NOT_VISIBLE, bounds.width,
+                bounds.height, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
         frame.setBounds(bounds);
         frame.setVisible(true);
       }
index a1693f7..01c8a16 100755 (executable)
@@ -121,8 +121,8 @@ public class Finder extends GFinder
               }
             });
     addEscapeHandler();
-    Desktop.addInternalFrame(frame, MessageManager.getString("label.find"),
-            true, MY_WIDTH, MY_HEIGHT, true, true);
+    Desktop.addInternalFrame(frame, MessageManager.getString("label.find"), 
+            Desktop.FRAME_MAKE_VISIBLE, MY_WIDTH, MY_HEIGHT, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_ALLOW_ANY_SIZE);
     searchBox.getComponent().requestFocus();
   }
 
@@ -176,7 +176,7 @@ public class Finder extends GFinder
    */
   boolean getFocusedViewport()
   {
-    if (focusfixed || Desktop.desktop == null)
+    if (focusfixed || Desktop.getDesktopPane() == null)
     {
       if (ap != null && av != null)
       {
@@ -187,7 +187,7 @@ public class Finder extends GFinder
     }
     // now checks further down the window stack to fix bug
     // https://mantis.lifesci.dundee.ac.uk/view.php?id=36008
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
     for (int f = 0; f < frames.length; f++)
     {
       JInternalFrame alignFrame = frames[f];
index 92cc4c6..2d9682b 100755 (executable)
@@ -137,14 +137,14 @@ public class FontChooser extends GFontChooser
     if (isTreeFont())
     {
       Desktop.addInternalFrame(frame,
-              MessageManager.getString("action.change_font_tree_panel"),
-              400, 200, false);
+              MessageManager.getString("action.change_font_tree_panel"), Desktop.FRAME_MAKE_VISIBLE,
+              400, 200, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_SET_MIN_SIZE_300);
     }
     else
     {
       Desktop.addInternalFrame(frame,
-              MessageManager.getString("action.change_font"), 380, 220,
-              false);
+              MessageManager.getString("action.change_font"), Desktop.FRAME_MAKE_VISIBLE, 380, 220,
+              Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_SET_MIN_SIZE_300);
     }
 
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
index 10c0787..b883e98 100755 (executable)
  */
 package jalview.gui;
 
+import jalview.datamodel.SequenceI;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Font;
@@ -33,10 +37,6 @@ import java.util.List;
 
 import javax.swing.JPanel;
 
-import jalview.datamodel.SequenceI;
-import jalview.viewmodel.ViewportListenerI;
-import jalview.viewmodel.ViewportRanges;
-
 /**
  * DOCUMENT ME!
  * 
@@ -59,7 +59,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
 
   int imgHeight = 0;
 
-  boolean fastPaint = false;
+  private boolean fastPaint = false;
 
   List<SequenceI> searchResults;
 
@@ -67,6 +67,8 @@ public class IdCanvas extends JPanel implements ViewportListenerI
 
   private Font idfont;
 
+  private boolean allowFastPaint;
+
   /**
    * Creates a new IdCanvas object.
    * 
@@ -137,6 +139,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     g.drawString(s.getDisplayId(av.getShowJVSuffix()), xPos,
             (((i - starty + 1) * charHeight) + ypos) - (charHeight / 5));
 
+    // JAL-3253-applet was just hiddenRows here. ccfc48e (gmungoc) added getShowHiddenMarkers test
     if (hiddenRows && av.getShowHiddenMarkers())
     {
       drawMarker(g, av, i, starty, ypos);
@@ -221,10 +224,15 @@ public class IdCanvas extends JPanel implements ViewportListenerI
   @Override
   public void paintComponent(Graphics g)
   {
+    if (av.getAlignPanel().getHoldRepaint())
+    {
+      return;
+    }
+
     g.setColor(Color.white);
     g.fillRect(0, 0, getWidth(), getHeight());
     
-    if (fastPaint)
+    if (allowFastPaint && fastPaint)
     {
       fastPaint = false;
       g.drawImage(image, 0, 0, this);
@@ -244,7 +252,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     
     if (oldHeight != imgHeight || image.getWidth(this) != getWidth())
     {
-       image = new BufferedImage(getWidth(), imgHeight,
+      image = new BufferedImage(getWidth(), imgHeight,
                 BufferedImage.TYPE_INT_RGB);
     }
     
@@ -384,20 +392,49 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     int alignmentWidth = alignViewport.getAlignment().getWidth();
     final int alheight = alignViewport.getAlignment().getHeight();
 
-    /*
+    
+//    int annotationHeight = 0;
+
+    /* (former)
      * assumption: SeqCanvas.calculateWrappedGeometry has been called
+     * 
+     * was based on the fact that SeqCanvas was added as a child prior to IdCanvas, 
+     * and children are processed in order of addition.
+     * 
+     * It's probably fine. But...
+     * 
      */
     SeqCanvas seqCanvas = alignViewport.getAlignPanel()
             .getSeqPanel().seqCanvas;
+    // ...better: let's call it now
+    seqCanvas.calculateWrappedGeometry();
 
     final int charHeight = alignViewport.getCharHeight();
 
     AnnotationLabels labels = null;
     if (alignViewport.isShowAnnotation())
     {
+       // BH when was ap == null?
+      if (ap == null)
+      {
+        ap = new AnnotationPanel(alignViewport);
+      }
+//      annotationHeight = ap.adjustPanelHeight();
       labels = new AnnotationLabels(alignViewport);
     }
 
+//    int hgap = charHeight;
+//    if (alignViewport.getScaleAboveWrapped())
+//    {
+//      hgap += charHeight;
+//    }
+//
+//    /*
+//     * height of alignment + gap + annotations (if shown)
+//     */
+//    int cHeight = alheight * charHeight + hgap
+//            + annotationHeight;
+//
     ViewportRanges ranges = alignViewport.getRanges();
 
     int rowSize = ranges.getViewportWidth();
@@ -568,20 +605,34 @@ public class IdCanvas extends JPanel implements ViewportListenerI
   public void propertyChange(PropertyChangeEvent evt)
   {
     String propertyName = evt.getPropertyName();
-    if (propertyName.equals(ViewportRanges.STARTSEQ)
-            || (av.getWrapAlignment()
-                    && propertyName.equals(ViewportRanges.STARTRES)))
+    switch (propertyName)
     {
+    case ViewportRanges.STARTSEQ:
       fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
-    }
-    else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ))
-    {
+      break;
+    case ViewportRanges.STARTRES:
+      if (av.getWrapAlignment())
+      {
+        fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
+      }
+      break;
+    case ViewportRanges.STARTRESANDSEQ:
       fastPaint(((int[]) evt.getNewValue())[1]
               - ((int[]) evt.getOldValue())[1]);
-    }
-    else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT))
-    {
+      break;
+    case ViewportRanges.MOVE_VIEWPORT:
       repaint();
+      break;
+    default:
     }
   }
+
+  /**
+   * Clears the flag that allows a 'fast paint' on the next repaint, so
+   * requiring a full repaint
+   */
+  public void setNoFastPaint()
+  {
+    allowFastPaint = false;
+  }
 }
index 4b5e9d4..2734b8d 100755 (executable)
@@ -242,7 +242,7 @@ public class IdPanel extends JPanel
       jalview.util.BrowserLauncher.openURL(url);
     } catch (Exception ex)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager.getString("label.web_browser_not_found_unix"),
               MessageManager.getString("label.web_browser_not_found"),
               JvOptionPane.WARNING_MESSAGE);
index 1d7bf3d..0ff5606 100644 (file)
@@ -78,11 +78,11 @@ public abstract class JalviewDialog extends JPanel
           boolean block, String title, int width, int height)
   {
 
-    frame = new JDialog(Desktop.instance, modal);
+    frame = new JDialog(Desktop.getInstance(), modal);
     frame.setTitle(title);
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
-      Rectangle deskr = Desktop.instance.getBounds();
+      Rectangle deskr = Desktop.getInstance().getBounds();
       frame.setBounds(new Rectangle((int) (deskr.getCenterX() - width / 2),
               (int) (deskr.getCenterY() - height / 2), width, height));
     }
index 25ba68d..8a735ed 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.gui;
 
-import jalview.util.MessageManager;
-
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Container;
@@ -44,6 +42,9 @@ import javax.swing.SwingConstants;
 import javax.swing.border.Border;
 import javax.swing.border.TitledBorder;
 
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 /**
  * useful functions for building Swing GUIs
  * 
@@ -52,12 +53,18 @@ import javax.swing.border.TitledBorder;
  */
 public final class JvSwingUtils
 {
+  static final String HTML_PREFIX = (Platform.isJS() ? 
+          "<html><div style=\"max-width:350px;overflow-wrap:break-word;display:inline-block\">"
+          : "<html><div style=\"width:350; text-align: justify; word-wrap: break-word;\">"
+            );
+
   /**
    * wrap a bare html safe string to around 60 characters per line using a CSS
    * style class specifying word-wrap and break-word
    * 
    * @param enclose
-   *          if true, add &lt;html&gt; wrapper tags
+   *          if true, add &lt;html&gt; wrapper tags (currently false for only
+   *          two references -- both in Jws2Discoverer --
    * @param ttext
    * 
    * @return
@@ -66,40 +73,40 @@ public final class JvSwingUtils
   {
     Objects.requireNonNull(ttext,
             "Tootip text to format must not be null!");
-    ttext = ttext.trim();
-    boolean maxLengthExceeded = false;
+    ttext = ttext.trim().replaceAll("<br/>", "<br>");
 
-    if (ttext.contains("<br>"))
+    boolean maxLengthExceeded = false;
+    boolean isHTML = ttext.startsWith("<html>");
+    if (isHTML)
     {
-      String[] htmllines = ttext.split("<br>");
-      for (String line : htmllines)
-      {
-        maxLengthExceeded = line.length() > 60;
-        if (maxLengthExceeded)
-        {
+      ttext = ttext.substring(6);
+    }
+    if (ttext.endsWith("</html>"))
+    {
+      isHTML = true;
+      ttext = ttext.substring(0, ttext.length() - 7);
+    }
+    boolean hasBR = ttext.contains("<br>");
+    enclose |= isHTML || hasBR;
+    if (hasBR)
+    {  
+      int pt = -1, ptlast = -4;
+      while ((pt = ttext.indexOf("<br>", pt + 1)) >= 0) {
+        if (pt - ptlast - 4 > 60) {
+          maxLengthExceeded = true;
           break;
         }
       }
     }
-    else
+    else  
     {
       maxLengthExceeded = ttext.length() > 60;
     }
 
-    if (!maxLengthExceeded)
-    {
-      return enclose ? "<html>" + ttext + "</html>" : ttext;
-    }
-
-    return (enclose ? "<html>" : "")
-     // BH 2018
-            + "<style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style><div class=\"ttip\">"
-//            + "<style> p.ttip {width:350px;margin:-14px 0px -14px 0px;padding:2px;overflow-wrap:break-word;}"
-//            + "</style><p class=\"ttip\">"
-            + ttext
-            + " </div>"
-//            + "</p>"
-            + ((enclose ? "</html>" : ""));
+    String ret = (!enclose ? ttext : maxLengthExceeded ? HTML_PREFIX + ttext + "</div></html>" :
+      "<html>" + ttext + "</html>");
+    //System.out.println("JvSwUtil " + enclose + " " + maxLengthExceeded + " " + ret);
+    return ret;
   }
 
   public static JButton makeButton(String label, String tooltip,
index d55733c..62bdd35 100644 (file)
@@ -86,7 +86,7 @@ public class LineartOptions extends JPanel
       ex.printStackTrace();
     }
 
-    dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+    dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane());
   }
 
   /**
@@ -188,7 +188,7 @@ public class LineartOptions extends JPanel
     }
     else
     {
-      Cache.applicationProperties.remove(preferencesKey);
+      Cache.removePropertyNoSave(preferencesKey);
     }
   }
 
index 02c8fe1..dc5d0f5 100644 (file)
@@ -72,7 +72,7 @@ public class OOMWarning implements Runnable
 
   public OOMWarning(String string, OutOfMemoryError oomerror)
   {
-    this(string, oomerror, Desktop.desktop);
+    this(string, oomerror, Desktop.getDesktopPane());
   }
 
   @Override
index a215b9a..4b6d79b 100755 (executable)
@@ -74,34 +74,41 @@ public class OverviewPanel extends JPanel
 
   protected boolean draggingBox = false;
 
+  private Dimension dim;
+  
+  private boolean showProgress = !Platform.isJS();
+
   protected ProgressPanel progressPanel;
 
+  
   /**
-   * Creates a new OverviewPanel object.
-   * 
-   * @param alPanel
-   *          The alignment panel which is shown in the overview panel
+   * Creates the appropriate type of OverviewDimensions, with the desired size
    */
-  public OverviewPanel(AlignmentPanel alPanel)
+  private void createOverviewDimensions()
   {
-    this.av = alPanel.av;
-    this.ap = alPanel;
-
-    showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START,
-            false);
+    boolean showAnnotation = (av.isShowAnnotation()
+            && av.getAlignmentConservationAnnotation() != null);
     if (showHidden)
     {
-      od = new OverviewDimensionsShowHidden(av.getRanges(),
-            (av.isShowAnnotation()
-                    && av.getAlignmentConservationAnnotation() != null));
+      od = new OverviewDimensionsShowHidden(av.getRanges(), showAnnotation,
+              dim);
     }
     else
     {
-      od = new OverviewDimensionsHideHidden(av.getRanges(),
-              (av.isShowAnnotation()
-                      && av.getAlignmentConservationAnnotation() != null));
+      od = new OverviewDimensionsHideHidden(av.getRanges(), showAnnotation,
+              dim);
     }
+  }
 
+  public OverviewPanel(AlignmentPanel alPanel, Dimension dim)
+  {
+    this.av = alPanel.av;
+    this.ap = alPanel;
+    this.dim = dim;
+
+    showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START,
+            false);
+    createOverviewDimensions();
     setLayout(new BorderLayout());
     progressPanel = new ProgressPanel(OverviewRenderer.UPDATE,
             MessageManager.getString("label.oview_calc"), getWidth());
@@ -286,20 +293,8 @@ public class OverviewPanel extends JPanel
    */
   protected void toggleHiddenColumns()
   {
-    if (showHidden)
-    {
-      showHidden = false;
-      od = new OverviewDimensionsHideHidden(av.getRanges(),
-              (av.isShowAnnotation()
-                      && av.getAlignmentConservationAnnotation() != null));
-    }
-    else
-    {
-      showHidden = true;
-      od = new OverviewDimensionsShowHidden(av.getRanges(),
-              (av.isShowAnnotation()
-                      && av.getAlignmentConservationAnnotation() != null));
-    }
+    showHidden = !showHidden;
+    createOverviewDimensions();
     oviewCanvas.resetOviewDims(od);
     updateOverviewImage();
     setBoxPosition();
index a2114f0..c0d57a6 100644 (file)
@@ -36,6 +36,7 @@ import jalview.jbgui.GPCAPanel;
 import jalview.math.RotatableMatrix.Axis;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.PCAModel;
 
@@ -200,10 +201,7 @@ public class PCAPanel extends GPCAPanel
     repaint();
     if (getParent() == null)
     {
-      Desktop.addInternalFrame(this,
-              MessageManager.formatMessage("label.calc_title", "PCA",
-                      getPcaModel().getScoreModelName()),
-              475, 450);
+      addToDesktop(this, getPcaModel().getScoreModelName());
       this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
     }
     working = false;
@@ -768,4 +766,12 @@ public class PCAPanel extends GPCAPanel
     getRotatableCanvas().ap = panel;
     PaintRefresher.Register(PCAPanel.this, panel.av.getSequenceSetId());
   }
+
+  public static void addToDesktop(PCAPanel panel, String modelName)
+  {
+    Dimension dim = Platform.getDimIfEmbedded(panel, 475, 450);
+    Desktop.addInternalFrame(panel, MessageManager.formatMessage(
+            "label.calc_title", "PCA", modelName), dim.width,
+            dim.height);
+  }
 }
index 953fdc5..7e203b2 100755 (executable)
@@ -39,6 +39,9 @@ import java.util.Map;
  */
 public class PaintRefresher
 {
+  private static final int ALIGNMENT_CHANGED = 1 << 0;
+  private static final int VALIDATE_SEQUENCES = 1 << 1;
+  
   static Map<String, List<Component>> components = new HashMap<>();
 
   /**
@@ -101,25 +104,32 @@ public class PaintRefresher
   {
     List<Component> comps = components.get(id);
 
+    int mode = (alignmentChanged ? ALIGNMENT_CHANGED : 0) | (validateSequences ? VALIDATE_SEQUENCES : 0);
     if (comps == null)
     {
       return;
     }
+    repaintComponents(source, mode, comps.toArray(new Component[comps.size()]));
+  }
 
-    for (Component comp : comps)
+  public static void repaintComponents(Component source, int mode,
+          Component... comps)
+  {
+    for (int i = 0; i < comps.length; i++)
     {
-      if (comp == source)
+      Component comp = comps[i];
+      if (comp == null)
       {
         continue;
       }
       if (comp instanceof AlignmentPanel)
       {
-        if (validateSequences && source instanceof AlignmentPanel)
+        if ((mode & VALIDATE_SEQUENCES) != 0 && source instanceof AlignmentPanel)
         {
           validateSequences(((AlignmentPanel) source).av.getAlignment(),
                   ((AlignmentPanel) comp).av.getAlignment());
         }
-        if (alignmentChanged)
+        if ((mode & ALIGNMENT_CHANGED) != 0)
         {
           ((AlignmentPanel) comp).alignmentChanged();
         }
@@ -128,13 +138,13 @@ public class PaintRefresher
       {
         // BH 2019.04.22 fixes JS problem of repaint() consolidation
         // that occurs in JavaScript but not Java [JAL-3226]
-        ((IdCanvas) comp).fastPaint = false;
+        ((IdCanvas) comp).setNoFastPaint();
       }
       else if (comp instanceof SeqCanvas)
       {
         // BH 2019.04.22 fixes JS problem of repaint() consolidation
         // that occurs in JavaScript but not Java [JAL-3226]
-        ((SeqCanvas) comp).fastPaint = false;
+        ((SeqCanvas) comp).setNoFastPaint();
       }
       comp.repaint();
     }
@@ -262,4 +272,5 @@ public class PaintRefresher
     return tmp.toArray(new AlignmentPanel[tmp.size()]);
   }
 
+  
 }
index 2fd5180..f46bd4e 100644 (file)
@@ -306,7 +306,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       jalview.util.BrowserLauncher.openURL(url);
     } catch (Exception ex)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager.getString("label.web_browser_not_found_unix"),
               MessageManager.getString("label.web_browser_not_found"),
               JvOptionPane.WARNING_MESSAGE);
@@ -1735,7 +1735,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       menuItem.setEnabled(true);
       for (String calcId : tipEntries.keySet())
       {
-        tooltip.append("<br/>" + calcId + "/" + tipEntries.get(calcId));
+        tooltip.append("<br>" + calcId + "/" + tipEntries.get(calcId));
       }
       String tooltipText = JvSwingUtils.wrapTooltip(true,
               tooltip.toString());
@@ -2076,8 +2076,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
                   ap.paintAlignment(false, false);
                 }
                 sequence.setDescription(dialog.getDescription());
-                ap.av.firePropertyChange("alignment", null,
-                        ap.av.getAlignment().getSequences());
+                ap.av.notifyAlignment();
               }
             });
   }
@@ -2119,7 +2118,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         refresh();
       }
     };
-    JalviewColourChooser.showColourChooser(Desktop.getDesktop(),
+    JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(),
             title, Color.BLUE, listener);
   }
 
@@ -2208,9 +2207,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
               startEnd, caseChange);
 
       ap.alignFrame.addHistoryItem(caseCommand);
+      ap.av.notifyAlignment();
 
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
 
     }
   }
@@ -2317,8 +2315,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
                           sg.getStartRes(), sg.getEndRes() + 1,
                           ap.av.getAlignment());
                   ap.alignFrame.addHistoryItem(editCommand);
-                  ap.av.firePropertyChange("alignment", null,
-                          ap.av.getAlignment().getSequences());
+                  ap.av.notifyAlignment();
                 }
               });
     }
index 50ee476..04851e8 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
-import jalview.bin.Cache;
-import jalview.gui.Help.HelpId;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.hmmer.HmmerCommand;
-import jalview.io.BackupFiles;
-import jalview.io.BackupFilesPresetEntry;
-import jalview.io.FileFormatI;
-import jalview.io.JalviewFileChooser;
-import jalview.io.JalviewFileView;
-import jalview.jbgui.GPreferences;
-import jalview.jbgui.GSequenceLink;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ColourSchemes;
-import jalview.schemes.ResidueColourScheme;
-import jalview.urls.UrlLinkTableModel;
-import jalview.urls.api.UrlProviderFactoryI;
-import jalview.urls.api.UrlProviderI;
-import jalview.urls.desktop.DesktopUrlProviderFactory;
-import jalview.util.FileUtils;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.util.UrlConstants;
-import jalview.ws.sifts.SiftsSettings;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -78,7 +53,32 @@ import javax.swing.table.TableColumn;
 import javax.swing.table.TableModel;
 import javax.swing.table.TableRowSorter;
 
+import jalview.hmmer.HmmerCommand;
+import jalview.util.FileUtils;
+
 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.bin.Cache;
+import jalview.gui.Help.HelpId;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.BackupFiles;
+import jalview.io.BackupFilesPresetEntry;
+import jalview.io.FileFormatI;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.jbgui.GPreferences;
+import jalview.jbgui.GSequenceLink;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemes;
+import jalview.schemes.ResidueColourScheme;
+import jalview.urls.UrlLinkTableModel;
+import jalview.urls.api.UrlProviderFactoryI;
+import jalview.urls.api.UrlProviderI;
+import jalview.urls.desktop.DesktopUrlProviderFactory;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.util.UrlConstants;
+import jalview.ws.sifts.SiftsSettings;
 
 /**
  * DOCUMENT ME!
@@ -96,28 +96,50 @@ public class Preferences extends GPreferences
   public static final String HMMINFO_GLOBAL_BACKGROUND = "HMMINFO_GLOBAL_BACKGROUND";
 
   public static final String HMMALIGN_TRIM_TERMINI = "HMMALIGN_TRIM_TERMINI";
+  
+  public static final String ADD_SS_ANN = "ADD_SS_ANN";
 
-  public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
+  public static final String ADD_TEMPFACT_ANN = "ADD_TEMPFACT_ANN";
 
-  public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
+  public static final String ALLOW_UNPUBLISHED_PDB_QUERYING = "ALLOW_UNPUBLISHED_PDB_QUERYING";
+
+  public static final String ANNOTATIONCOLOUR_MAX = "ANNOTATIONCOLOUR_MAX";
+
+  public static final String ANNOTATIONCOLOUR_MIN = "ANNOTATIONCOLOUR_MIN";
+
+  public static final String ANTI_ALIAS = "ANTI_ALIAS";
+
+  public static final String AUTO_CALC_CONSENSUS = "AUTO_CALC_CONSENSUS";
+
+  public static final String AUTOASSOCIATE_PDBANDSEQS = "AUTOASSOCIATE_PDBANDSEQS";
+
+  public static final String BLOSUM62_PCA_FOR_NUCLEOTIDE = "BLOSUM62_PCA_FOR_NUCLEOTIDE";
+
+  public static final String CENTRE_COLUMN_LABELS = "CENTRE_COLUMN_LABELS";
+
+  public static final String CHIMERA_PATH = "CHIMERA_PATH";
+
+  public static final String DBREFFETCH_USEPICR = "DBREFFETCH_USEPICR";
 
   public static final String DEFAULT_COLOUR = "DEFAULT_COLOUR";
 
+  public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
+
   public static final String DEFAULT_COLOUR_PROT = "DEFAULT_COLOUR_PROT";
 
-  public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
+  public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
 
-  public static final String ADD_TEMPFACT_ANN = "ADD_TEMPFACT_ANN";
+  public static final String FIGURE_AUTOIDWIDTH = "FIGURE_AUTOIDWIDTH";
 
-  public static final String ADD_SS_ANN = "ADD_SS_ANN";
+  public static final String FIGURE_FIXEDIDWIDTH = "FIGURE_FIXEDIDWIDTH";
 
-  public static final String USE_RNAVIEW = "USE_RNAVIEW";
+  public static final String FOLLOW_SELECTIONS = "FOLLOW_SELECTIONS";
 
-  public static final String STRUCT_FROM_PDB = "STRUCT_FROM_PDB";
+  public static final String FONT_NAME = "FONT_NAME";
 
-  public static final String STRUCTURE_DISPLAY = "STRUCTURE_DISPLAY";
+  public static final String FONT_SIZE = "FONT_SIZE";
 
-  public static final String CHIMERA_PATH = "CHIMERA_PATH";
+  public static final String FONT_STYLE = "FONT_STYLE";
   
   public static final String HMMER_PATH = "HMMER_PATH";
 
@@ -125,19 +147,94 @@ public class Preferences extends GPreferences
 
   public static final String HMMSEARCH_DBS = "HMMSEARCH_DBS";
 
-  public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
+  public static final String GAP_COLOUR = "GAP_COLOUR";
+
+  public static final String GAP_SYMBOL = "GAP_SYMBOL";
+
+  public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
+
+  public static final String HIDE_INTRONS = "HIDE_INTRONS";
+
+  public static final String ID_ITALICS = "ID_ITALICS";
+
+  public static final String ID_ORG_HOSTURL = "ID_ORG_HOSTURL";
+
+  public static final String MAP_WITH_SIFTS = "MAP_WITH_SIFTS";
+
+  public static final String NOQUESTIONNAIRES = "NOQUESTIONNAIRES";
+
+  public static final String NORMALISE_CONSENSUS_LOGO = "NORMALISE_CONSENSUS_LOGO";
+
+  public static final String NORMALISE_LOGO = "NORMALISE_LOGO";
+
+  public static final String PAD_GAPS = "PAD_GAPS";
+
+  public static final String PDB_DOWNLOAD_FORMAT = "PDB_DOWNLOAD_FORMAT";
+
+  public static final String QUESTIONNAIRE = "QUESTIONNAIRE";
+
+  public static final String RELAXEDSEQIDMATCHING = "RELAXEDSEQIDMATCHING";
+
+  public static final String RIGHT_ALIGN_IDS = "RIGHT_ALIGN_IDS";
+
+  public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
+
+  public static final String SHOW_ANNOTATIONS = "SHOW_ANNOTATIONS";
 
   public static final String SHOW_AUTOCALC_ABOVE = "SHOW_AUTOCALC_ABOVE";
 
+  public static final String SHOW_CONSENSUS = "SHOW_CONSENSUS";
+
+  public static final String SHOW_CONSENSUS_HISTOGRAM = "SHOW_CONSENSUS_HISTOGRAM";
+
+  public static final String SHOW_CONSENSUS_LOGO = "SHOW_CONSENSUS_LOGO";
+
+  public static final String SHOW_CONSERVATION = "SHOW_CONSERVATION";
+
+  public static final String SHOW_DBREFS_TOOLTIP = "SHOW_DBREFS_TOOLTIP";
+
+  public static final String SHOW_GROUP_CONSENSUS = "SHOW_GROUP_CONSENSUS";
+
+  public static final String SHOW_GROUP_CONSERVATION = "SHOW_GROUP_CONSERVATION";
+
+  public static final String SHOW_JVSUFFIX = "SHOW_JVSUFFIX";
+
+  public static final String SHOW_NPFEATS_TOOLTIP = "SHOW_NPFEATS_TOOLTIP";
+
   public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY";
 
   public static final String SHOW_OV_HIDDEN_AT_START = "SHOW_OV_HIDDEN_AT_START";
 
+  public static final String SHOW_OVERVIEW = "SHOW_OVERVIEW";
+
+  public static final String SHOW_QUALITY = "SHOW_QUALITY";
+
+  public static final String SHOW_UNCONSERVED = "SHOW_UNCONSERVED";
+
+  public static final String SORT_ALIGNMENT = "SORT_ALIGNMENT";
+
+  public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
+
+  public static final String SORT_BY_TREE = "SORT_BY_TREE";
+
+  public static final String STRUCT_FROM_PDB = "STRUCT_FROM_PDB";
+
+  public static final String STRUCTURE_DISPLAY = "STRUCTURE_DISPLAY";
+
+  public static final String STRUCTURE_DIMENSIONS = "STRUCTURE_DIMENSIONS";
+
+  public static final String UNIPROT_DOMAIN = "UNIPROT_DOMAIN";
+
+  public static final String USE_FULL_SO = "USE_FULL_SO";
+
   public static final String USE_LEGACY_GAP = "USE_LEGACY_GAP";
 
-  public static final String GAP_COLOUR = "GAP_COLOUR";
+  public static final String USE_RNAVIEW = "USE_RNAVIEW";
+
+  public static final String USER_DEFINED_COLOURS = "USER_DEFINED_COLOURS";
+
+  public static final String WRAP_ALIGNMENT = "WRAP_ALIGNMENT";
 
-  public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
 
   private static final int MIN_FONT_SIZE = 1;
 
@@ -431,8 +528,11 @@ public class Preferences extends GPreferences
     addSecondaryStructure.setEnabled(structSelected);
     addTempFactor.setSelected(Cache.getDefault(ADD_TEMPFACT_ANN, false));
     addTempFactor.setEnabled(structSelected);
-    structViewer.setSelectedItem(
+    if (!Platform.isJS())
+    {
+      structViewer.setSelectedItem(
             Cache.getDefault(STRUCTURE_DISPLAY, ViewerType.JMOL.name()));
+    }
     chimeraPath.setText(Cache.getDefault(CHIMERA_PATH, ""));
     chimeraPath.addActionListener(new ActionListener()
     {
@@ -684,69 +784,69 @@ public class Preferences extends GPreferences
     /*
      * Save Visual settings
      */
-    Cache.applicationProperties.setProperty("SHOW_JVSUFFIX",
+    Cache.setPropertyNoSave("SHOW_JVSUFFIX",
             Boolean.toString(seqLimit.isSelected()));
-    Cache.applicationProperties.setProperty("RIGHT_ALIGN_IDS",
+    Cache.setPropertyNoSave("RIGHT_ALIGN_IDS",
             Boolean.toString(rightAlign.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_FULLSCREEN",
+    Cache.setPropertyNoSave("SHOW_FULLSCREEN",
             Boolean.toString(fullScreen.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_OVERVIEW",
+    Cache.setPropertyNoSave("SHOW_OVERVIEW",
             Boolean.toString(openoverv.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
             Boolean.toString(annotations.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+    Cache.setPropertyNoSave("SHOW_CONSERVATION",
             Boolean.toString(conservation.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_QUALITY",
+    Cache.setPropertyNoSave("SHOW_QUALITY",
             Boolean.toString(quality.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.toString(identity.isSelected()));
 
-    Cache.applicationProperties.setProperty("GAP_SYMBOL",
+    Cache.setPropertyNoSave("GAP_SYMBOL",
             gapSymbolCB.getSelectedItem().toString());
 
-    Cache.applicationProperties.setProperty("FONT_NAME",
+    Cache.setPropertyNoSave("FONT_NAME",
             fontNameCB.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty("FONT_STYLE",
+    Cache.setPropertyNoSave("FONT_STYLE",
             fontStyleCB.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty("FONT_SIZE",
+    Cache.setPropertyNoSave("FONT_SIZE",
             fontSizeCB.getSelectedItem().toString());
 
-    Cache.applicationProperties.setProperty("ID_ITALICS",
+    Cache.setPropertyNoSave("ID_ITALICS",
             Boolean.toString(idItalics.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_UNCONSERVED",
+    Cache.setPropertyNoSave("SHOW_UNCONSERVED",
             Boolean.toString(showUnconserved.isSelected()));
-    Cache.applicationProperties.setProperty(SHOW_OCCUPANCY,
+    Cache.setPropertyNoSave(SHOW_OCCUPANCY,
             Boolean.toString(showOccupancy.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_GROUP_CONSENSUS",
+    Cache.setPropertyNoSave("SHOW_GROUP_CONSENSUS",
             Boolean.toString(showGroupConsensus.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_GROUP_CONSERVATION",
+    Cache.setPropertyNoSave("SHOW_GROUP_CONSERVATION",
             Boolean.toString(showGroupConservation.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSENSUS_HISTOGRAM",
+    Cache.setPropertyNoSave("SHOW_CONSENSUS_HISTOGRAM",
             Boolean.toString(showConsensHistogram.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_CONSENSUS_LOGO",
+    Cache.setPropertyNoSave("SHOW_CONSENSUS_LOGO",
             Boolean.toString(showConsensLogo.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_INFORMATION_HISTOGRAM",
+    Cache.setPropertyNoSave("SHOW_INFORMATION_HISTOGRAM",
             Boolean.toString(showConsensHistogram.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_HMM_LOGO",
+    Cache.setPropertyNoSave("SHOW_HMM_LOGO",
             Boolean.toString(showHMMLogo.isSelected()));
-    Cache.applicationProperties.setProperty("ANTI_ALIAS",
+    Cache.setPropertyNoSave("ANTI_ALIAS",
             Boolean.toString(smoothFont.isSelected()));
-    Cache.applicationProperties.setProperty(SCALE_PROTEIN_TO_CDNA,
+    Cache.setPropertyNoSave(SCALE_PROTEIN_TO_CDNA,
             Boolean.toString(scaleProteinToCdna.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_NPFEATS_TOOLTIP",
+    Cache.setPropertyNoSave("SHOW_NPFEATS_TOOLTIP",
             Boolean.toString(showNpTooltip.isSelected()));
-    Cache.applicationProperties.setProperty("SHOW_DBREFS_TOOLTIP",
+    Cache.setPropertyNoSave("SHOW_DBREFS_TOOLTIP",
             Boolean.toString(showDbRefTooltip.isSelected()));
 
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT",
+    Cache.setPropertyNoSave("WRAP_ALIGNMENT",
             Boolean.toString(wrap.isSelected()));
 
-    Cache.applicationProperties.setProperty("STARTUP_FILE",
+    Cache.setPropertyNoSave("STARTUP_FILE",
             startupFileTextfield.getText());
-    Cache.applicationProperties.setProperty("SHOW_STARTUP_FILE",
+    Cache.setPropertyNoSave("SHOW_STARTUP_FILE",
             Boolean.toString(startupCheckbox.isSelected()));
 
-    Cache.applicationProperties.setProperty("SORT_ALIGNMENT",
+    Cache.setPropertyNoSave("SORT_ALIGNMENT",
             sortby.getSelectedItem().toString());
 
     // convert description of sort order to enum name for save
@@ -754,34 +854,34 @@ public class Preferences extends GPreferences
             .forDescription(sortAnnBy.getSelectedItem().toString());
     if (annSortOrder != null)
     {
-      Cache.applicationProperties.setProperty(SORT_ANNOTATIONS,
+      Cache.setPropertyNoSave(SORT_ANNOTATIONS,
               annSortOrder.name());
     }
 
     final boolean showAutocalcFirst = sortAutocalc.getSelectedIndex() == 0;
-    Cache.applicationProperties.setProperty(SHOW_AUTOCALC_ABOVE,
+    Cache.setPropertyNoSave(SHOW_AUTOCALC_ABOVE,
             Boolean.valueOf(showAutocalcFirst).toString());
 
     /*
      * Save Colours settings
      */
-    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_PROT,
+    Cache.setPropertyNoSave(DEFAULT_COLOUR_PROT,
             protColour.getSelectedItem().toString());
-    Cache.applicationProperties.setProperty(DEFAULT_COLOUR_NUC,
+    Cache.setPropertyNoSave(DEFAULT_COLOUR_NUC,
             nucColour.getSelectedItem().toString());
-    Cache.setColourProperty("ANNOTATIONCOLOUR_MIN",
+    Cache.setColourPropertyNoSave("ANNOTATIONCOLOUR_MIN",
             minColour.getBackground());
-    Cache.setColourProperty("ANNOTATIONCOLOUR_MAX",
+    Cache.setColourPropertyNoSave("ANNOTATIONCOLOUR_MAX",
             maxColour.getBackground());
 
     /*
      * Save HMMER settings
      */
-    Cache.applicationProperties.setProperty(HMMALIGN_TRIM_TERMINI,
+    Cache.setPropertyNoSave(HMMALIGN_TRIM_TERMINI,
             Boolean.toString(hmmrTrimTermini.isSelected()));
-    Cache.applicationProperties.setProperty(HMMINFO_GLOBAL_BACKGROUND,
+    Cache.setPropertyNoSave(HMMINFO_GLOBAL_BACKGROUND,
             Boolean.toString(hmmerBackgroundUniprot.isSelected()));
-    Cache.applicationProperties.setProperty(HMMSEARCH_SEQCOUNT,
+    Cache.setPropertyNoSave(HMMSEARCH_SEQCOUNT,
             hmmerSequenceCount.getText());
     Cache.setOrRemove(HMMER_PATH, hmmerPath.getText());
     if (cygwinPath != null)
@@ -813,39 +913,42 @@ public class Preferences extends GPreferences
     /*
      * Save Overview settings
      */
-    Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground());
-    Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground());
-    Cache.applicationProperties.setProperty(USE_LEGACY_GAP,
+    Cache.setColourPropertyNoSave(GAP_COLOUR, gapColour.getBackground());
+    Cache.setColourPropertyNoSave(HIDDEN_COLOUR, hiddenColour.getBackground());
+    Cache.setPropertyNoSave(USE_LEGACY_GAP,
             Boolean.toString(useLegacyGap.isSelected()));
-    Cache.applicationProperties.setProperty(SHOW_OV_HIDDEN_AT_START,
+    Cache.setPropertyNoSave(SHOW_OV_HIDDEN_AT_START,
             Boolean.toString(showHiddenAtStart.isSelected()));
 
     /*
      * Save Structure settings
      */
-    Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN,
+    Cache.setPropertyNoSave(ADD_TEMPFACT_ANN,
             Boolean.toString(addTempFactor.isSelected()));
-    Cache.applicationProperties.setProperty(ADD_SS_ANN,
+    Cache.setPropertyNoSave(ADD_SS_ANN,
             Boolean.toString(addSecondaryStructure.isSelected()));
-    Cache.applicationProperties.setProperty(USE_RNAVIEW,
+    Cache.setPropertyNoSave(USE_RNAVIEW,
             Boolean.toString(useRnaView.isSelected()));
-    Cache.applicationProperties.setProperty(STRUCT_FROM_PDB,
+    Cache.setPropertyNoSave(STRUCT_FROM_PDB,
             Boolean.toString(structFromPdb.isSelected()));
-    Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY,
-            structViewer.getSelectedItem().toString());
+    if (!Platform.isJS())
+    {
+      Cache.setPropertyNoSave(STRUCTURE_DISPLAY,
+              structViewer.getSelectedItem().toString());
+    }
     Cache.setOrRemove(CHIMERA_PATH, chimeraPath.getText());
-    Cache.applicationProperties.setProperty("MAP_WITH_SIFTS",
+    Cache.setPropertyNoSave("MAP_WITH_SIFTS",
             Boolean.toString(siftsMapping.isSelected()));
     SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
 
     /*
      * Save Output settings
      */
-    Cache.applicationProperties.setProperty("EPS_RENDERING",
+    Cache.setPropertyNoSave("EPS_RENDERING",
             ((OptionsParam) epsRendering.getSelectedItem()).getCode());
-    Cache.applicationProperties.setProperty("HTML_RENDERING",
+    Cache.setPropertyNoSave("HTML_RENDERING",
             ((OptionsParam) htmlRendering.getSelectedItem()).getCode());
-    Cache.applicationProperties.setProperty("SVG_RENDERING",
+    Cache.setPropertyNoSave("SVG_RENDERING",
             ((OptionsParam) svgRendering.getSelectedItem()).getCode());
 
     /*
@@ -859,29 +962,29 @@ public class Preferences extends GPreferences
     String menuLinks = sequenceUrlLinks.writeUrlsAsString(true);
     if (menuLinks.isEmpty())
     {
-      Cache.applicationProperties.remove("SEQUENCE_LINKS");
+      Cache.removePropertyNoSave("SEQUENCE_LINKS");
     }
     else
     {
-      Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
+      Cache.setPropertyNoSave("SEQUENCE_LINKS",
               menuLinks.toString());
     }
 
     String nonMenuLinks = sequenceUrlLinks.writeUrlsAsString(false);
     if (nonMenuLinks.isEmpty())
     {
-      Cache.applicationProperties.remove("STORED_LINKS");
+      Cache.removePropertyNoSave("STORED_LINKS");
     }
     else
     {
-      Cache.applicationProperties.setProperty("STORED_LINKS",
+      Cache.setPropertyNoSave("STORED_LINKS",
               nonMenuLinks.toString());
     }
 
-    Cache.applicationProperties.setProperty("DEFAULT_URL",
+    Cache.setPropertyNoSave("DEFAULT_URL",
             sequenceUrlLinks.getPrimaryUrlId());
 
-    Cache.applicationProperties.setProperty("USE_PROXY",
+    Cache.setPropertyNoSave("USE_PROXY",
             Boolean.toString(useProxy.isSelected()));
 
     Cache.setOrRemove("PROXY_SERVER", proxyServerTB.getText());
@@ -920,40 +1023,40 @@ public class Preferences extends GPreferences
     /*
      * Save Output settings
      */
-    Cache.applicationProperties.setProperty("BLC_JVSUFFIX",
+    Cache.setPropertyNoSave("BLC_JVSUFFIX",
             Boolean.toString(blcjv.isSelected()));
-    Cache.applicationProperties.setProperty("CLUSTAL_JVSUFFIX",
+    Cache.setPropertyNoSave("CLUSTAL_JVSUFFIX",
             Boolean.toString(clustaljv.isSelected()));
-    Cache.applicationProperties.setProperty("FASTA_JVSUFFIX",
+    Cache.setPropertyNoSave("FASTA_JVSUFFIX",
             Boolean.toString(fastajv.isSelected()));
-    Cache.applicationProperties.setProperty("MSF_JVSUFFIX",
+    Cache.setPropertyNoSave("MSF_JVSUFFIX",
             Boolean.toString(msfjv.isSelected()));
-    Cache.applicationProperties.setProperty("PFAM_JVSUFFIX",
+    Cache.setPropertyNoSave("PFAM_JVSUFFIX",
             Boolean.toString(pfamjv.isSelected()));
-    Cache.applicationProperties.setProperty("PILEUP_JVSUFFIX",
+    Cache.setPropertyNoSave("PILEUP_JVSUFFIX",
             Boolean.toString(pileupjv.isSelected()));
-    Cache.applicationProperties.setProperty("PIR_JVSUFFIX",
+    Cache.setPropertyNoSave("PIR_JVSUFFIX",
             Boolean.toString(pirjv.isSelected()));
-    Cache.applicationProperties.setProperty("PIR_MODELLER",
+    Cache.setPropertyNoSave("PIR_MODELLER",
             Boolean.toString(modellerOutput.isSelected()));
-    Cache.applicationProperties.setProperty("EXPORT_EMBBED_BIOJSON",
+    Cache.setPropertyNoSave("EXPORT_EMBBED_BIOJSON",
             Boolean.toString(embbedBioJSON.isSelected()));
     jalview.io.PIRFile.useModellerOutput = modellerOutput.isSelected();
 
-    Cache.applicationProperties.setProperty("FIGURE_AUTOIDWIDTH",
+    Cache.setPropertyNoSave("FIGURE_AUTOIDWIDTH",
             Boolean.toString(autoIdWidth.isSelected()));
     userIdWidth_actionPerformed();
-    Cache.applicationProperties.setProperty("FIGURE_FIXEDIDWIDTH",
+    Cache.setPropertyNoSave("FIGURE_FIXEDIDWIDTH",
             userIdWidth.getText());
 
     /*
      * Save Editing settings
      */
-    Cache.applicationProperties.setProperty("AUTO_CALC_CONSENSUS",
+    Cache.setPropertyNoSave("AUTO_CALC_CONSENSUS",
             Boolean.toString(autoCalculateConsCheck.isSelected()));
-    Cache.applicationProperties.setProperty("SORT_BY_TREE",
+    Cache.setPropertyNoSave("SORT_BY_TREE",
             Boolean.toString(sortByTree.isSelected()));
-    Cache.applicationProperties.setProperty("PAD_GAPS",
+    Cache.setPropertyNoSave("PAD_GAPS",
             Boolean.toString(padGaps.isSelected()));
 
     if (!Platform.isJS())
@@ -964,28 +1067,27 @@ public class Preferences extends GPreferences
     /*
      * Save Backups settings
      */
-    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+    Cache.setPropertyNoSave(BackupFiles.ENABLED,
             Boolean.toString(enableBackupFiles.isSelected()));
     int preset = getComboIntStringKey(backupfilesPresetsCombo);
-    Cache.applicationProperties.setProperty(BackupFiles.NS + "_PRESET", Integer.toString(preset));
+    Cache.setPropertyNoSave(BackupFiles.NS + "_PRESET", Integer.toString(preset));
 
     if (preset == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
     {
       BackupFilesPresetEntry customBFPE = getBackupfilesCurrentEntry();
       BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
               BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM, customBFPE);
-      Cache.applicationProperties
-              .setProperty(BackupFilesPresetEntry.CUSTOMCONFIG,
+      Cache.setPropertyNoSave(BackupFilesPresetEntry.CUSTOMCONFIG,
                       customBFPE.toString());
     }
 
     BackupFilesPresetEntry savedBFPE = BackupFilesPresetEntry.backupfilesPresetEntriesValues
             .get(preset);
-    Cache.applicationProperties.setProperty(
+    Cache.setPropertyNoSave(
             BackupFilesPresetEntry.SAVEDCONFIG, savedBFPE.toString());
 
     Cache.saveProperties();
-    Desktop.instance.doConfigureStructurePrefs();
+    Desktop.getInstance().doConfigureStructurePrefs();
     try
     {
       frame.setClosed(true);
@@ -994,7 +1096,43 @@ public class Preferences extends GPreferences
     }
   }
 
-  /**
+  public static void setAppletDefaults()
+  {
+
+    // http://www.jalview.org/old/v2_8/examples/appletParameters.html
+
+    // showConservation true or false Default is true.
+    // showQuality true or false Default is true.
+    // showConsensus true or false Default is true.
+    // showFeatureSettings true or false Shows the feature settings window when
+    // startin
+    // showTreeBootstraps true or false (default is true) show or hide branch
+    // bootstraps
+    // showTreeDistances true or false (default is true) show or hide branch
+    // lengths
+    // showUnlinkedTreeNodes true or false (default is false) indicate if
+    // unassociated nodes should be highlighted in the tree view
+    // showUnconserved true of false (default is false) When true, only gaps and
+    // symbols different to the consensus sequence ions of the alignment
+    // showGroupConsensus true of false (default is false) When true, shows
+    // consensus annotation row for any groups on the alignment. (since 2.7)
+    // showGroupConservation true of false (default is false) When true, shows
+    // amino-acid property conservation annotation row for any groups on the
+    // showConsensusHistogram true of false (default is true) When true, shows
+    // the percentage occurence of the consensus symbol for each column as a
+    // showSequenceLogo true of false (default is false) When true, shows a
+    // sequence logo above the consensus sequence (overlaid above the Consensus
+
+    Cache.setPropertyNoSave(SHOW_CONSERVATION, "true");
+    Cache.setPropertyNoSave(SHOW_QUALITY, "false");
+    Cache.setPropertyNoSave(SHOW_CONSENSUS, "true");
+    Cache.setPropertyNoSave(SHOW_UNCONSERVED, "false");
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSERVATION, "false");
+    Cache.setPropertyNoSave(SHOW_GROUP_CONSENSUS, "false");
+
+    // TODO -- just a start here
+  }
+ /**
    * Do any necessary validation before saving settings. Return focus to the
    * first tab which fails validation.
    * 
@@ -1038,7 +1176,7 @@ public class Preferences extends GPreferences
       FileFormatI format = chooser.getSelectedFormat();
       if (format != null)
       {
-        Cache.applicationProperties.setProperty("DEFAULT_FILE_FORMAT",
+        Cache.setPropertyNoSave("DEFAULT_FILE_FORMAT",
                 format.getName());
       }
       startupFileTextfield
@@ -1098,7 +1236,7 @@ public class Preferences extends GPreferences
     boolean valid = false;
     while (!valid)
     {
-      if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
+      if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
               MessageManager.getString("label.new_sequence_url_link"),
               JvOptionPane.OK_CANCEL_OPTION, -1,
               null) == JvOptionPane.OK_OPTION)
@@ -1150,7 +1288,7 @@ public class Preferences extends GPreferences
     boolean valid = false;
     while (!valid)
     {
-      if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
+      if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
               MessageManager.getString("label.edit_sequence_url_link"),
               JvOptionPane.OK_CANCEL_OPTION, -1,
               null) == JvOptionPane.OK_OPTION)
@@ -1323,7 +1461,7 @@ public class Preferences extends GPreferences
     } catch (NumberFormatException x)
     {
       userIdWidth.setText("");
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("warn.user_defined_width_requirements"),
               MessageManager.getString("label.invalid_id_column_width"),
@@ -1349,7 +1487,7 @@ public class Preferences extends GPreferences
       File f = new File(chimeraPath.getText());
       if (!f.canExecute())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.invalid_chimera_path"),
                 MessageManager.getString("label.invalid_name"),
                 JvOptionPane.ERROR_MESSAGE);
@@ -1378,7 +1516,7 @@ public class Preferences extends GPreferences
     }
     if (folder.length() > 0)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getInstance(),
               MessageManager.formatMessage("label.executable_not_found",
                       executable),
               MessageManager.getString("label.invalid_folder"),
@@ -1439,7 +1577,7 @@ public class Preferences extends GPreferences
     if (!found)
     {
       String[] options = { "OK", "Help" };
-      int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.desktop,
+      int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.getDesktopPane(),
               JvSwingUtils.wrapTooltip(true,
                       MessageManager.getString("label.chimera_missing")),
               "", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
index cb59452..77d83a8 100644 (file)
@@ -200,7 +200,7 @@ public class PromptUserConfig implements Runnable
     }
     try
     {
-      int reply = JvOptionPane.showConfirmDialog(Desktop.desktop, // component,
+      int reply = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(), // component,
               dialogText, dialogTitle,
               (allowCancel) ? JvOptionPane.YES_NO_CANCEL_OPTION
                       : JvOptionPane.YES_NO_OPTION,
index 6ed3248..c7e2b8e 100755 (executable)
@@ -103,8 +103,8 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
     frame.setContentPane(this);
     Desktop.addInternalFrame(frame,
             MessageManager
-                    .getString("label.redundancy_threshold_selection"),
-            true, FRAME_WIDTH, FRAME_HEIGHT, false, true);
+                    .getString("label.redundancy_threshold_selection"), Desktop.FRAME_MAKE_VISIBLE,
+            FRAME_WIDTH, FRAME_HEIGHT, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_ALLOW_ANY_SIZE);
     frame.addInternalFrameListener(new InternalFrameAdapter()
     {
       @Override
@@ -263,8 +263,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
       ap.alignFrame.addHistoryItem(cut);
 
       PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
     }
 
   }
@@ -289,8 +288,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
     {
       command.undoCommand(af.getViewAlignments());
       ap.av.getHistoryList().remove(command);
-      ap.av.firePropertyChange("alignment", null,
-              ap.av.getAlignment().getSequences());
+      ap.av.notifyAlignment();
       af.updateEditMenuBar();
     }
 
index 50b84e7..34d456f 100644 (file)
  */
 package jalview.gui;
 
-import jalview.jbgui.GRestInputParamEditDialog;
-import jalview.ws.params.InvalidArgumentException;
-import jalview.ws.params.OptionI;
-import jalview.ws.params.ParameterI;
-import jalview.ws.rest.InputType;
-import jalview.ws.rest.RestServiceDescription;
-
 import java.util.ArrayList;
 import java.util.Hashtable;
 
 import javax.swing.JPanel;
 import javax.swing.event.ListSelectionEvent;
 
+import jalview.jbgui.GRestInputParamEditDialog;
+import jalview.ws.params.InvalidArgumentException;
+import jalview.ws.params.OptionI;
+import jalview.ws.params.ParameterI;
+import jalview.ws.rest.InputType;
+import jalview.ws.rest.RestServiceDescription;
 import net.miginfocom.swing.MigLayout;
 
 public class RestInputParamEditDialog extends GRestInputParamEditDialog
index a6b4b49..fda1e03 100755 (executable)
@@ -180,9 +180,7 @@ public class ScalePanel extends JPanel
         {
           av.showColumn(hiddenRange[0]);
           reveal = null;
-          ap.updateLayout();
-          ap.paintAlignment(true, true);
-          av.sendSelection();
+          updatePanel();
         }
       });
       pop.add(item);
@@ -197,9 +195,7 @@ public class ScalePanel extends JPanel
           {
             av.showAllHiddenColumns();
             reveal = null;
-            ap.updateLayout();
-            ap.paintAlignment(true, true);
-            av.sendSelection();
+            updatePanel();
           }
         });
         pop.add(item);
@@ -221,10 +217,7 @@ public class ScalePanel extends JPanel
           {
             av.setSelectionGroup(null);
           }
-
-          ap.updateLayout();
-          ap.paintAlignment(true, true);
-          av.sendSelection();
+          updatePanel();
         }
       });
       pop.add(item);
@@ -232,6 +225,14 @@ public class ScalePanel extends JPanel
     return pop;
   }
 
+  protected void updatePanel()
+  {
+    ap.updateLayout();
+    ap.paintAlignment(true, true);
+    ap.updateScrollBarsFromRanges();
+    av.sendSelection();
+  }
+
   /**
    * Handles left mouse button press
    * 
index b27208a..59a6cb1 100755 (executable)
@@ -57,7 +57,8 @@ import javax.swing.JPanel;
 public class SeqCanvas extends JPanel implements ViewportListenerI
 {
   /**
-   * vertical gap in pixels between sequences and annotations when in wrapped mode
+   * vertical gap in pixels between sequences and annotations when in wrapped
+   * mode
    */
   static final int SEQS_ANNOTATION_GAP = 3;
 
@@ -75,7 +76,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
   private final SequenceRenderer seqRdr;
 
-  boolean fastPaint = false;
+  private boolean fastPaint = false;
 
   private boolean fastpainting = false;
 
@@ -94,8 +95,14 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
   private int wrappedVisibleWidths; // number of wrapped widths displayed
 
+  private int availWidth;
+
+  private int availHeight;
+
+  private boolean allowFastPaint;
+
   // Don't do this! Graphics handles are supposed to be transient
-  //private Graphics2D gg;
+  // private Graphics2D gg;
 
   /**
    * Creates a new SeqCanvas object.
@@ -116,7 +123,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
   public SequenceRenderer getSequenceRenderer()
   {
-    return seqRdr; 
+    return seqRdr;
   }
 
   public FeatureRenderer getFeatureRenderer()
@@ -202,7 +209,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     int yPos = ypos + charHeight;
     int startX = startx;
     int endX = endx;
-    
+
     if (av.hasHiddenColumns())
     {
       HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
@@ -238,7 +245,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
         }
       }
 
-      
       /*
        * white fill the space for the scale
        */
@@ -343,7 +349,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
         }
       }
 
-
       // System.err.println(">>> FastPaint to " + transX + " " + transY + " "
       // + horizontal + " " + vertical + " " + startRes + " " + endRes
       // + " " + startSeq + " " + endSeq);
@@ -352,9 +357,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       gg.copyArea(horizontal * charWidth, vertical * charHeight,
               img.getWidth(), img.getHeight(), -horizontal * charWidth,
               -vertical * charHeight);
-
-      /** @j2sNative xxi = this.img */
-
       gg.translate(transX, transY);
       drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
       gg.translate(-transX, -transY);
@@ -363,7 +365,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       // Call repaint on alignment panel so that repaints from other alignment
       // panel components can be aggregated. Otherwise performance of the
       // overview window and others may be adversely affected.
-      // System.out.println("SeqCanvas fastPaint() repaint() request...");
       av.getAlignPanel().repaint();
     } finally
     {
@@ -374,23 +375,17 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
   @Override
   public void paintComponent(Graphics g)
   {
+    if (av.getAlignPanel().getHoldRepaint())
+    {
+      return;
+    }
 
-    int charHeight = av.getCharHeight();
-    int charWidth = av.getCharWidth();
-
-    int width = getWidth();
-    int height = getHeight();
-
-    width -= (width % charWidth);
-    height -= (height % charHeight);
-
-    // BH 2019 can't possibly fastPaint if either width or height is 0
+    getAvailSizes();
 
-    if (width == 0 || height == 0)
+    if (availWidth == 0 || availHeight == 0)
     {
       return;
     }
-
     ViewportRanges ranges = av.getRanges();
     int startRes = ranges.getStartRes();
     int startSeq = ranges.getStartSeq();
@@ -420,11 +415,9 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     // }
 
     Rectangle vis, clip;
-    if (img != null
-            && (fastPaint
-                    || (vis = getVisibleRect()).width != (clip = g
-                            .getClipBounds()).width
-                    || vis.height != clip.height))
+    if (allowFastPaint  && img != null
+            && (fastPaint || (vis = getVisibleRect()).width != (clip = g.getClipBounds()).width
+                          || vis.height != clip.height))
     {
       g.drawImage(img, 0, 0, this);
       drawSelectionGroup((Graphics2D) g, startRes, endRes, startSeq,
@@ -433,13 +426,15 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
     else
     {
+      allowFastPaint = true;
       // img is a cached version of the last view we drew.
       // If we have no img or the size has changed, make a new one.
       //
-      if (img == null || width != img.getWidth()
-              || height != img.getHeight())
+      if (img == null || availWidth != img.getWidth()
+              || availHeight != img.getHeight())
       {
-        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        img = new BufferedImage(availWidth, availHeight,
+                BufferedImage.TYPE_INT_RGB);
       }
 
       Graphics2D gg = (Graphics2D) img.getGraphics();
@@ -452,11 +447,11 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       }
 
       gg.setColor(Color.white);
-      gg.fillRect(0, 0, img.getWidth(), img.getHeight());
+      gg.fillRect(0, 0, availWidth, availHeight);
 
       if (av.getWrapAlignment())
       {
-        drawWrappedPanel(gg, width, height, ranges.getStartRes());
+        drawWrappedPanel(gg, availWidth, availHeight, ranges.getStartRes());
       }
       else
       {
@@ -474,7 +469,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       drawCursor(g, startRes, endRes, startSeq, endSeq);
     }
   }
-  
+
   /**
    * Draw an alignment panel for printing
    * 
@@ -494,8 +489,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
   {
     drawPanel(g1, startRes, endRes, startSeq, endSeq, 0);
 
-    drawSelectionGroup((Graphics2D) g1, startRes, endRes,
-            startSeq, endSeq);
+    drawSelectionGroup((Graphics2D) g1, startRes, endRes, startSeq, endSeq);
   }
 
   /**
@@ -519,37 +513,36 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     if (group != null)
     {
       drawWrappedSelection((Graphics2D) g, group, canvasWidth, canvasHeight,
-                startRes);
+              startRes);
     }
   }
 
   /**
-   * Returns the visible width of the canvas in residues, after allowing for
-   * East or West scales (if shown)
+   * Using the current font, determine fields labelWidthEast and labelWidthWest,
+   * and return the number of residues that can fill the remaining width
    * 
-   * @param canvasWidth
+   * @param w
    *          the width in pixels (possibly including scales)
    * 
-   * @return
+   * @return the visible width in residues, after allowing for East or West
+   *         scales (if shown)
+   * 
    */
-  public int getWrappedCanvasWidth(int canvasWidth)
+  public int getWrappedCanvasWidth(int w)
   {
     int charWidth = av.getCharWidth();
 
     FontMetrics fm = getFontMetrics(av.getFont());
 
-    int labelWidth = 0;
-    
-    if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
-    {
-      labelWidth = getLabelWidth(fm);
-    }
+    int labelWidth = (av.getScaleRightWrapped() || av.getScaleLeftWrapped()
+            ? getLabelWidth(fm)
+            : 0);
 
     labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0;
 
     labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0;
 
-    return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
+    return (w - labelWidthEast - labelWidthWest) / charWidth;
   }
 
   /**
@@ -575,6 +568,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd());
     }
 
+    // quick int log10
     int length = 0;
     for (int i = maxWidth; i > 0; i /= 10)
     {
@@ -589,28 +583,25 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
    * window
    * 
    * @param g
-   * @param canvasWidth
+   * @param availWidth
    *          available width in pixels
-   * @param canvasHeight
+   * @param availHeight
    *          available height in pixels
    * @param startColumn
    *          the first column (0...) of the alignment to draw
    */
-  public void drawWrappedPanel(Graphics g, int canvasWidth,
-          int canvasHeight, final int startColumn)
+  public void drawWrappedPanel(Graphics g, int availWidth, int availHeight,
+          final int startColumn)
   {
-    int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth,
-            canvasHeight);
-
+    int wrappedWidthInResidues = calculateWrappedGeometry();
     av.setWrappedWidth(wrappedWidthInResidues);
-
     ViewportRanges ranges = av.getRanges();
     ranges.setViewportStartAndWidth(startColumn, wrappedWidthInResidues);
 
     // we need to call this again to make sure the startColumn +
     // wrappedWidthInResidues values are used to calculate wrappedVisibleWidths
     // correctly.
-    calculateWrappedGeometry(canvasWidth, canvasHeight);
+    calculateWrappedGeometry();
 
     /*
      * draw one width at a time (excluding any scales shown),
@@ -623,9 +614,9 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     int currentWidth = 0;
     while ((currentWidth < wrappedVisibleWidths) && (start < maxWidth))
     {
-      int endColumn = Math
-              .min(maxWidth, start + wrappedWidthInResidues - 1);
-      drawWrappedWidth(g, ypos, start, endColumn, canvasHeight);
+      int endColumn = Math.min(maxWidth,
+              start + wrappedWidthInResidues - 1);
+      drawWrappedWidth(g, ypos, start, endColumn, availHeight);
       ypos += wrappedRepeatHeightPx;
       start += wrappedWidthInResidues;
       currentWidth++;
@@ -634,6 +625,16 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     drawWrappedDecorators(g, startColumn);
   }
 
+  private void getAvailSizes()
+  {
+    int charHeight = av.getCharHeight();
+    int charWidth = av.getCharWidth();
+    availWidth = getWidth();
+    availHeight = getHeight();
+    availWidth -= (availWidth % charWidth);
+    availHeight -= (availHeight % charHeight);
+  }
+
   /**
    * Calculates and saves values needed when rendering a wrapped alignment.
    * These depend on many factors, including
@@ -644,12 +645,26 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
    * <li>whether scales are shown left, right or above the alignment</li>
    * </ul>
    * 
+   * @param availWidth
+   * @param availHeight
+   * @return the number of residue columns in each width
+   */
+  protected int calculateWrappedGeometry()
+  {
+    getAvailSizes();
+    return calculateWrappedGeometry(availWidth, availHeight);
+
+  }
+
+  /**
+   * for test only
    * @param canvasWidth
    * @param canvasHeight
-   * @return the number of residue columns in each width
+   * @return
    */
-  protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight)
+  public int calculateWrappedGeometry(int canvasWidth, int canvasHeight)
   {
+
     int charHeight = av.getCharHeight();
 
     /*
@@ -663,9 +678,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
      * compute height in pixels of the wrapped widths
      * - start with space above plus sequences
      */
-    wrappedRepeatHeightPx = wrappedSpaceAboveAlignment;
-    wrappedRepeatHeightPx += av.getAlignment().getHeight()
-            * charHeight;
+    wrappedRepeatHeightPx = wrappedSpaceAboveAlignment
+            + av.getAlignment().getHeight() * charHeight;
 
     /*
      * add annotations panel height if shown
@@ -736,7 +750,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     int charWidth = av.getCharWidth();
     int xOffset = labelWidthWest
             + ((startColumn - ranges.getStartRes()) % viewportWidth)
-            * charWidth;
+                    * charWidth;
 
     g.translate(xOffset, 0);
 
@@ -803,7 +817,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       if (av.getScaleRightWrapped())
       {
         int x = labelWidthWest + viewportWidth * charWidth;
-        
+
         g.translate(x, 0);
         drawVerticalScale(g, startCol, endColumn, ypos, false);
         g.translate(-x, 0);
@@ -815,8 +829,9 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
        */
       g.translate(labelWidthWest, 0);
       g.setColor(Color.white);
-      g.fillRect(0, ypos - wrappedSpaceAboveAlignment, viewportWidth
-              * charWidth + labelWidthWest, wrappedSpaceAboveAlignment);
+      g.fillRect(0, ypos - wrappedSpaceAboveAlignment,
+              viewportWidth * charWidth + labelWidthWest,
+              wrappedSpaceAboveAlignment);
       g.setColor(Color.black);
       g.translate(-labelWidthWest, 0);
 
@@ -883,18 +898,21 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
   }
 
+  private final static BasicStroke dottedStroke = new BasicStroke(1,
+          BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 3f, new float[]
+          { 5f, 3f }, 0f);
+
+  private final static BasicStroke basicStroke = new BasicStroke();
+
   /*
    * Draw a selection group over a wrapped alignment
    */
   private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
-          int canvasWidth,
-          int canvasHeight, int startRes)
+          int canvasWidth, int canvasHeight, int startRes)
   {
     // chop the wrapped alignment extent up into panel-sized blocks and treat
     // each block as if it were a block from an unwrapped alignment
-    g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
-            BasicStroke.JOIN_ROUND, 3f, new float[]
-            { 5f, 3f }, 0f));
+    g.setStroke(dottedStroke);
     g.setColor(Color.RED);
 
     int charWidth = av.getCharWidth();
@@ -902,6 +920,21 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
             / charWidth;
     int startx = startRes;
     int maxwidth = av.getAlignment().getVisibleWidth();
+
+    // JAL-3253-applet had this:
+    // // height gap above each panel
+    // int charHeight = av.getCharHeight();
+    // int hgap = charHeight;
+    // if (av.getScaleAboveWrapped())
+    // {
+    // hgap += charHeight;
+    // }
+    // int dy = getAnnotationHeight() + hgap
+    // + av.getAlignment().getHeight() * charHeight;
+    // int ypos = hgap; // vertical offset
+
+    // this is from 0b573ed (gmungoc)
+    int dy = wrappedRepeatHeightPx;
     int ypos = wrappedSpaceAboveAlignment;
 
     while ((ypos <= canvasHeight) && (startx < maxwidth))
@@ -915,21 +948,24 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       }
 
       g.translate(labelWidthWest, 0);
+
       drawUnwrappedSelection(g, group, startx, endx, 0,
-              av.getAlignment().getHeight() - 1,
-              ypos);
+              av.getAlignment().getHeight() - 1, ypos);
+
       g.translate(-labelWidthWest, 0);
 
-      ypos += wrappedRepeatHeightPx;
+      // update vertical offset
+      ypos += dy;
 
+      // update horizontal offset
       startx += cWidth;
     }
-    g.setStroke(new BasicStroke());
+    g.setStroke(basicStroke);
   }
 
   /**
-   * Answers zero if annotations are not shown, otherwise recalculates and answers
-   * the total height of all annotation rows in pixels
+   * Answers zero if annotations are not shown, otherwise recalculates and
+   * answers the total height of all annotation rows in pixels
    * 
    * @return
    */
@@ -1190,8 +1226,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
    *         the cursor drawn on it, if any
    */
   private void drawCursor(Graphics g, int startRes, int endRes,
-          int startSeq,
-          int endSeq)
+          int startSeq, int endSeq)
   {
     // convert the cursorY into a position on the visible alignment
     int cursor_ypos = cursorY;
@@ -1262,7 +1297,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
   }
 
-
   /**
    * Draw a selection group over an unwrapped alignment
    * 
@@ -1285,7 +1319,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
           int startRes, int endRes, int startSeq, int endSeq, int offset)
   {
     int charWidth = av.getCharWidth();
-          
+
     if (!av.hasHiddenColumns())
     {
       drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
@@ -1308,8 +1342,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
         blockStart = region[0];
 
         g.translate(screenY * charWidth, 0);
-        drawPartialGroupOutline(g, group,
-                blockStart, blockEnd, startSeq, endSeq, offset);
+        drawPartialGroupOutline(g, group, blockStart, blockEnd, startSeq,
+                endSeq, offset);
 
         g.translate(-screenY * charWidth, 0);
         screenY += blockEnd - blockStart + 1;
@@ -1478,7 +1512,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
     }
   }
-  
+
   /**
    * Highlights search results in the visible region by rendering as white text
    * on a black background. Any previous highlighting is removed. Answers true
@@ -1494,7 +1528,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     return highlightSearchResults(results, false);
 
   }
-  
+
   /**
    * Highlights search results in the visible region by rendering as white text
    * on a black background. Any previous highlighting is removed. Answers true
@@ -1652,7 +1686,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       {
         firstCol = alignment.getHiddenColumns()
                 .absoluteToVisibleColumn(firstCol);
-        lastCol = alignment.getHiddenColumns().absoluteToVisibleColumn(lastCol);
+        lastCol = alignment.getHiddenColumns()
+                .absoluteToVisibleColumn(lastCol);
       }
       int transX = (firstCol - ranges.getStartRes()) * av.getCharWidth();
       int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight();
@@ -1670,92 +1705,158 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
   public void propertyChange(PropertyChangeEvent evt)
   {
     String eventName = evt.getPropertyName();
-    // System.err.println(">>SeqCanvas propertyChange " + eventName);
-    if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
-    {
-      fastPaint = true;
-      repaint();
-      return;
-    }
-    else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
-    {
-      fastPaint = false;
-      // System.err.println("!!!! fastPaint false from MOVE_VIEWPORT");
-      repaint();
-      return;
-    }
 
-    int scrollX = 0;
-    if (eventName.equals(ViewportRanges.STARTRES)
-            || eventName.equals(ViewportRanges.STARTRESANDSEQ))
-    {
-      // Make sure we're not trying to draw a panel
-      // larger than the visible window
-      if (eventName.equals(ViewportRanges.STARTRES))
-      {
-        scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
-      }
-      else
-      {
-        scrollX = ((int[]) evt.getNewValue())[0]
-                - ((int[]) evt.getOldValue())[0];
-      }
-      ViewportRanges vpRanges = av.getRanges();
+    // BH 2019.07.27 removes dead code introduced in aad3650 and simplifies
+    // logic, emphasizing no check for ENDRES or ENDSEQ
 
-      int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
-      if (scrollX > range)
-      {
-        scrollX = range;
-      }
-      else if (scrollX < -range)
-      {
-        scrollX = -range;
-      }
-    }
     // Both scrolling and resizing change viewport ranges: scrolling changes
     // both start and end points, but resize only changes end values.
     // Here we only want to fastpaint on a scroll, with resize using a normal
     // paint, so scroll events are identified as changes to the horizontal or
     // vertical start value.
-    if (eventName.equals(ViewportRanges.STARTRES))
-    {
-      if (av.getWrapAlignment())
-      {
-        fastPaintWrapped(scrollX);
-      }
-      else
-      {
-        fastPaint(scrollX, 0);
-      }
-    }
-    else if (eventName.equals(ViewportRanges.STARTSEQ))
+
+    // Make sure we're not trying to draw a panel
+    // larger than the visible window
+    int scrollX = 0;
+    int scrollY = 0;
+    switch (eventName)
     {
-      // scroll
+    case SequenceGroup.SEQ_GROUP_CHANGED:
+      fastPaint = true;
+      repaint();
+      return;
+    case ViewportRanges.MOVE_VIEWPORT:
+      fastPaint = false;
+      repaint();
+      return;
+    case ViewportRanges.STARTSEQ:
+      // meaning STARTOREND
+      // typically scroll, but possibly just the end changed
       fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
-    }
-    else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
-    {
-      if (av.getWrapAlignment())
-      {
-        fastPaintWrapped(scrollX);
-      }
-      else
+      return;
+    case ViewportRanges.STARTRES:
+      // meaning STARTOREND
+      scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+      break;
+    case ViewportRanges.STARTRESANDSEQ:
+      scrollX = ((int[]) evt.getNewValue())[0]
+              - ((int[]) evt.getOldValue())[0];
+      scrollY = ((int[]) evt.getNewValue())[1]
+              - ((int[]) evt.getOldValue())[1];
+      if (scrollX != 0 && scrollY != 0)
       {
-        fastPaint(scrollX, 0);
+        // all sorts of problems in JavaScript if this is commented out.
+        repaint();
+        return;
+
       }
+      break;
+    default:
+      return;
     }
-    else if (eventName.equals(ViewportRanges.STARTSEQ))
+
+    ViewportRanges vpRanges = av.getRanges();
+    int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
+    scrollX = Math.max(Math.min(scrollX, range), -range);
+    // only STARTRES or STARTRESANDSEQ:
+    if (av.getWrapAlignment())
     {
-      // scroll
-      fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+      fastPaintWrapped(scrollX);
     }
-    else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    else
     {
-      if (av.getWrapAlignment())
-      {
-        fastPaintWrapped(scrollX);
-      }
+      fastPaint(scrollX, scrollY);
     }
+
+    // BH 2019.07.27 was:
+    // if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
+    // {
+    // fastPaint = true;
+    // repaint();
+    // return;
+    // }
+    // else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
+    // {
+    // fastPaint = false;
+    // // System.err.println("!!!! fastPaint false from MOVE_VIEWPORT");
+    // repaint();
+    // return;
+    // }
+    //
+    // if (eventName.equals(ViewportRanges.STARTRES)
+    // || eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    // {
+    // // Make sure we're not trying to draw a panel
+    // // larger than the visible window
+    // if (eventName.equals(ViewportRanges.STARTRES))
+    // {
+    // scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+    // }
+    // else
+    // {
+    // scrollX = ((int[]) evt.getNewValue())[0]
+    // - ((int[]) evt.getOldValue())[0];
+    // }
+    // ViewportRanges vpRanges = av.getRanges();
+    //
+    // int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
+    // if (scrollX > range)
+    // {
+    // scrollX = range;
+    // }
+    // else if (scrollX < -range)
+    // {
+    // scrollX = -range;
+    // }
+    // }
+    // Both scrolling and resizing change viewport ranges: scrolling changes
+    // both start and end points, but resize only changes end values.
+    // Here we only want to fastpaint on a scroll, with resize using a normal
+    // paint, so scroll events are identified as changes to the horizontal or
+    // vertical start value.
+    // BH 2019.07.27 was:
+    // if (eventName.equals(ViewportRanges.STARTRES))
+    // {
+    // if (av.getWrapAlignment())
+    // {
+    // fastPaintWrapped(scrollX);
+    // }
+    // else
+    // {
+    // fastPaint(scrollX, 0);
+    // }
+    // }
+    // else if (eventName.equals(ViewportRanges.STARTSEQ))
+    // {
+    // // scroll
+    // fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+    // }
+    // else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    // {
+    // if (av.getWrapAlignment())
+    // {
+    // fastPaintWrapped(scrollX);
+    // }
+    // else
+    // {
+    // fastPaint(scrollX, 0);
+    // }
+    // }
+    //
+    // BH oops!
+    //
+    // else if (eventName.equals(ViewportRanges.STARTSEQ))
+    // {
+    // // scroll
+    // fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+    // }
+    // else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    // {
+    // if (av.getWrapAlignment())
+    // {
+    // fastPaintWrapped(scrollX);
+    // }
+    // }
   }
 
   /**
@@ -1792,10 +1893,10 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
     try
     {
-      
+
       Graphics gg = img.getGraphics();
-      
-      calculateWrappedGeometry(getWidth(), getHeight());
+
+      calculateWrappedGeometry();
 
       /*
        * relocate the regions of the alignment that are still visible
@@ -1810,8 +1911,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       if (scrollX < 0)
       {
         int startRes = ranges.getStartRes();
-        drawWrappedWidth(gg, wrappedSpaceAboveAlignment, startRes, startRes
-                - scrollX - 1, getHeight());
+        drawWrappedWidth(gg, wrappedSpaceAboveAlignment, startRes,
+                startRes - scrollX - 1, getHeight());
       }
       else
       {
@@ -1824,7 +1925,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       drawWrappedDecorators(gg, ranges.getStartRes());
 
       gg.dispose();
-      
+
       repaint();
     } finally
     {
@@ -1849,7 +1950,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
 
     Graphics gg = img.getGraphics();
-    
+
     ViewportRanges ranges = av.getRanges();
     int viewportWidth = ranges.getViewportWidth();
     int charWidth = av.getCharWidth();
@@ -1860,7 +1961,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
      */
     int visibleWidths = wrappedVisibleWidths;
     int canvasHeight = getHeight();
-    boolean lastWidthPartHeight = (wrappedVisibleWidths * wrappedRepeatHeightPx) > canvasHeight;
+    boolean lastWidthPartHeight = (wrappedVisibleWidths
+            * wrappedRepeatHeightPx) > canvasHeight;
 
     if (lastWidthPartHeight)
     {
@@ -1876,16 +1978,15 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       /*
        * white fill first to erase annotations
        */
-      
-      
+
       gg.translate(xOffset, 0);
       gg.setColor(Color.white);
-      gg.fillRect(labelWidthWest, ypos,
-              (endRes - startRes + 1) * charWidth, wrappedRepeatHeightPx);
+      gg.fillRect(labelWidthWest, ypos, (endRes - startRes + 1) * charWidth,
+              wrappedRepeatHeightPx);
       gg.translate(-xOffset, 0);
 
       drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight);
-      
+
     }
 
     /*
@@ -1930,7 +2031,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
       gg.fillRect(0, canvasHeight - heightBelow, getWidth(), heightBelow);
     }
     gg.dispose();
- }
+  }
 
   /**
    * Shifts the visible alignment by the specified number of columns - left if
@@ -2018,8 +2119,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
         if (y + wrappedRepeatHeightPx < canvasHeight - wrappedRepeatHeightPx
                 && (xpos + viewportWidth <= xMax))
         {
-          gg.copyArea(labelWidthWest, y + wrappedRepeatHeightPx, -positions
-                  * charWidth, heightToCopy, widthToCopy,
+          gg.copyArea(labelWidthWest, y + wrappedRepeatHeightPx,
+                  -positions * charWidth, heightToCopy, widthToCopy,
                   -wrappedRepeatHeightPx);
         }
         y += wrappedRepeatHeightPx;
@@ -2029,7 +2130,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     gg.dispose();
   }
 
-  
   /**
    * Redraws any positions in the search results in the visible region of a
    * wrapped alignment. Any highlights are drawn depending on the search results
@@ -2050,7 +2150,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
     boolean matchFound = false;
 
-    calculateWrappedGeometry(getWidth(), getHeight());
+    calculateWrappedGeometry();
     int wrappedWidth = av.getWrappedWidth();
     int wrappedHeight = wrappedRepeatHeightPx;
 
@@ -2063,8 +2163,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     }
 
     int firstVisibleColumn = ranges.getStartRes();
-    int lastVisibleColumn = ranges.getStartRes() + repeats
-            * ranges.getViewportWidth() - 1;
+    int lastVisibleColumn = ranges.getStartRes()
+            + repeats * ranges.getViewportWidth() - 1;
 
     AlignmentI alignment = av.getAlignment();
     if (av.hasHiddenColumns())
@@ -2077,7 +2177,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
     int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
 
-    
     Graphics gg = img.getGraphics();
 
     for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
@@ -2121,8 +2220,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
                * transX: offset from left edge of canvas to residue position
                */
               int transX = labelWidthWest
-                      + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
-                      * av.getCharWidth();
+                      + ((displayColumn - ranges.getStartRes())
+                              % wrappedWidth) * av.getCharWidth();
 
               /*
                * transY: offset from top edge of canvas to residue position
@@ -2149,7 +2248,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
         }
       }
     }
-  
+
     gg.dispose();
 
     return matchFound;
@@ -2165,4 +2264,13 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
     return labelWidthWest;
   }
 
+  /**
+   * Clears the flag that allows a 'fast paint' on the next repaint, so
+   * requiring a full repaint
+   */
+  public void setNoFastPaint()
+  {
+    allowFastPaint = false;
+  }
+
 }
index d22ddd4..fdb75a4 100644 (file)
@@ -87,6 +87,7 @@ public class SeqPanel extends JPanel
         SequenceListener, SelectionListener
 {
   /*
+   * 
    * a class that holds computed mouse position
    * - column of the alignment (0...)
    * - sequence offset (0...)
@@ -242,6 +243,7 @@ public class SeqPanel extends JPanel
    */
   public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
   {
+    setName("SeqPanel");
     seqARep = new SequenceAnnotationReport(true);
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
@@ -290,8 +292,7 @@ public class SeqPanel extends JPanel
     int alignmentHeight = av.getAlignment().getHeight();
     if (av.getWrapAlignment())
     {
-      seqCanvas.calculateWrappedGeometry(seqCanvas.getWidth(),
-              seqCanvas.getHeight());
+      seqCanvas.calculateWrappedGeometry();
 
       /*
        * yPos modulo height of repeating width
@@ -423,8 +424,7 @@ public class SeqPanel extends JPanel
       if (editCommand != null && editCommand.getSize() > 0)
       {
         ap.alignFrame.addHistoryItem(editCommand);
-        av.firePropertyChange("alignment", null,
-                av.getAlignment().getSequences());
+        ap.av.notifyAlignment();
       }
     } finally
     {
@@ -1141,7 +1141,8 @@ public class SeqPanel extends JPanel
 
     String tooltip = AnnotationPanel.buildToolTip(anns[rowIndex], column,
             anns);
-    if (!tooltip.equals(lastTooltip))
+    boolean tooltipChanged = tooltip == null ? lastTooltip != null : !tooltip.equals(lastTooltip);
+    if (tooltipChanged)
     {
       lastTooltip = tooltip;
       lastFormattedTooltip = tooltip == null ? null
@@ -2899,4 +2900,45 @@ public class SeqPanel extends JPanel
   {
     return lastSearchResults;
   }
+  
+  /**
+   * scroll to the given row/column - or nearest visible location
+   * 
+   * @param row
+   * @param column
+   */
+  public void scrollTo(int row, int column)
+  {
+
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
+    ap.scrollTo(column, column, row, true, true);
+  }
+
+  /**
+   * scroll to the given row - or nearest visible location
+   * 
+   * @param row
+   */
+  public void scrollToRow(int row)
+  {
+
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    ap.scrollTo(ap.av.getRanges().getStartRes(),
+            ap.av.getRanges().getStartRes(), row, true, true);
+  }
+
+  /**
+   * scroll to the given column - or nearest visible location
+   * 
+   * @param column
+   */
+  public void scrollToColumn(int column)
+  {
+
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
+    ap.scrollTo(column, column, ap.av.getRanges().getStartSeq(), true,
+            true);
+  }
+
 }
index 8b5d3b7..3d09f99 100755 (executable)
@@ -145,8 +145,8 @@ public class SequenceFetcher extends JPanel implements Runnable
 
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    Desktop.addInternalFrame(frame, getFrameTitle(), true, 400, 
-               Platform.isAMacAndNotJS() ? 240 : 180);
+    Desktop.addInternalFrame(frame, getFrameTitle(), Desktop.FRAME_MAKE_VISIBLE, 400, 
+               Platform.isAMacAndNotJS() ? 240 : 180, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
   }
 
   private String getFrameTitle()
@@ -868,7 +868,7 @@ public class SequenceFetcher extends JPanel implements Runnable
       @Override
       public void run()
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop, error,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), error,
                 MessageManager.getString("label.error_retrieving_data"),
                 JvOptionPane.WARNING_MESSAGE);
       }
index dcc42ea..a577b69 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.analysis.Conservation;
-import jalview.datamodel.SequenceGroup;
-import jalview.jbgui.GSliderPanel;
-import jalview.renderer.ResidueShaderI;
-import jalview.util.MessageManager;
-
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.beans.PropertyVetoException;
@@ -38,6 +32,12 @@ import javax.swing.event.ChangeListener;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
+import jalview.analysis.Conservation;
+import jalview.datamodel.SequenceGroup;
+import jalview.jbgui.GSliderPanel;
+import jalview.renderer.ResidueShaderI;
+import jalview.util.MessageManager;
+
 /**
  * DOCUMENT ME!
  * 
@@ -230,8 +230,8 @@ public class SliderPanel extends GSliderPanel
     if (!conservationSlider.isVisible())
     {
       Desktop.addInternalFrame(conservationSlider,
-              conservationSlider.getTitle(), true, FRAME_WIDTH,
-              FRAME_HEIGHT, false, true);
+              conservationSlider.getTitle(), Desktop.FRAME_MAKE_VISIBLE, FRAME_WIDTH,
+              FRAME_HEIGHT, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_ALLOW_ANY_SIZE);
       conservationSlider.addInternalFrameListener(new InternalFrameAdapter()
       {
         @Override
@@ -306,8 +306,8 @@ public class SliderPanel extends GSliderPanel
 
     if (!PIDSlider.isVisible())
     {
-      Desktop.addInternalFrame(PIDSlider, PIDSlider.getTitle(), true,
-              FRAME_WIDTH, FRAME_HEIGHT, false, true);
+      Desktop.addInternalFrame(PIDSlider, PIDSlider.getTitle(), Desktop.FRAME_MAKE_VISIBLE,
+              FRAME_WIDTH, FRAME_HEIGHT, Desktop.FRAME_NOT_RESIZABLE, Desktop.FRAME_ALLOW_ANY_SIZE);
       PIDSlider.setLayer(JLayeredPane.PALETTE_LAYER);
       PIDSlider.addInternalFrameListener(new InternalFrameAdapter()
       {
index f4b275d..83346fc 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.util.Platform;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
-import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Graphics;
@@ -33,46 +30,50 @@ import java.awt.MediaTracker;
 import java.awt.Toolkit;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.net.URL;
 
 import javax.swing.JInternalFrame;
-import javax.swing.JLabel;
 import javax.swing.JLayeredPane;
 import javax.swing.JPanel;
 import javax.swing.JTextPane;
 import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkListener;
 
+import jalview.util.Platform;
+import javajs.async.SwingJSUtils.StateHelper;
+import javajs.async.SwingJSUtils.StateMachine;
+
 /**
  * DOCUMENT ME!
  * 
  * @author $author$
  * @version $Revision$
  */
+@SuppressWarnings("serial")
 public class SplashScreen extends JPanel
-        implements Runnable, HyperlinkListener
+        implements HyperlinkListener, StateMachine
 {
-  private static final int SHOW_FOR_SECS = 5;
+  
+  private static final int STATE_INIT = 0;
 
-  private static final int FONT_SIZE = 11;
+  private static final int STATE_LOOP = 1;
 
-  private boolean visible = true;
+  private static final int STATE_DONE = 2;
 
-  private JPanel iconimg = new JPanel(new BorderLayout());
+  private static final int SHOW_FOR_SECS = 5;
 
-  /*
-   * as JTextPane in Java, JLabel in javascript
-   */
-  private Component splashText;
+  private int FONT_SIZE = (Platform.isJS() ? 14 : 11);
+
+  private JPanel imgPanel = new JPanel(new BorderLayout());
 
   private JInternalFrame iframe;
 
-  private Image image;
+  private Image image, logo;
 
-  private boolean transientDialog = false;
+  protected boolean isStartup = false;
 
   private long oldTextLength = -1;
 
+  private StateHelper helper;
   /*
    * allow click in the initial splash screen to dismiss it
    * immediately (not if opened from About menu)
@@ -82,11 +83,10 @@ public class SplashScreen extends JPanel
     @Override
     public void mousePressed(MouseEvent evt)
     {
-      if (transientDialog)
+      if (isStartup)
       {
         try
         {
-          visible = false;
           closeSplash();
         } catch (Exception ex)
         {
@@ -98,201 +98,113 @@ public class SplashScreen extends JPanel
   /**
    * Constructor that displays the splash screen
    * 
-   * @param isTransient
+   * @param isStartup
    *          if true the panel removes itself on click or after a few seconds;
-   *          if false it stays up until closed by the user
+   *          if false it stays up until closed by the user (from Help..About menu)
    */
-  public SplashScreen(boolean isTransient)
+  public SplashScreen(boolean isStartup)
   {
-    this.transientDialog = isTransient;
-
-    if (Platform.isJS()) // BH 2019
-    {
-      splashText = new JLabel("");
-      run();
-    }
-    else
-    {
-      /**
-       * Java only
-       *
-       * @j2sIgnore
-       */
-      {
-        splashText = new JTextPane();
-        Thread t = new Thread(this);
-        t.start();
-      }
-    }
+    this.isStartup = isStartup;
+    // we must get the image in JavaScript BEFORE starting the helper,
+    // as it will take a 1 ms clock tick to obtain width and height information.
+    image = Toolkit.getDefaultToolkit().createImage(
+            getClass().getResource("/images/Jalview_Logo.png"));
+    helper = new StateHelper(this);
+    helper.next(STATE_INIT);
   }
 
-  /**
-   * ping the jalview version page then create and display the jalview
-   * splashscreen window.
-   */
-  void initSplashScreenWindow()
+  protected void initSplashScreenWindow()
   {
     addMouseListener(closer);
-
-    try
-    {
-      URL url = getClass().getResource("/images/Jalview_Logo.png");
-      URL urllogo = getClass()
-              .getResource("/images/Jalview_Logo_small.png");
-
-      if (!Platform.isJS() && url != null)
-      {
-        image = Toolkit.getDefaultToolkit().createImage(url);
-        Image logo = Toolkit.getDefaultToolkit().createImage(urllogo);
-        MediaTracker mt = new MediaTracker(this);
-        mt.addImage(image, 0);
-        mt.addImage(logo, 1);
-        do
-        {
-          try
-          {
-            mt.waitForAll();
-          } catch (InterruptedException x)
-          {
-          }
-          if (mt.isErrorAny())
-          {
-            System.err.println("Error when loading images!");
-          }
-        } while (!mt.checkAll());
-        Desktop.instance.setIconImage(logo);
-      }
-    } catch (Exception ex)
-    {
-    }
-
+    waitForImages();
+    setLayout(new BorderLayout());
     iframe = new JInternalFrame();
     iframe.setFrameIcon(null);
     iframe.setClosable(true);
-    this.setLayout(new BorderLayout());
     iframe.setContentPane(this);
-    iframe.setLayer(JLayeredPane.PALETTE_LAYER);
-    if (Platform.isJS())
-    {
-      // ignore in JavaScript
-    }
-    else
-    /**
-     * Java only
-     * 
-     * @j2sIgnore
-     */
-    {
-      ((JTextPane) splashText).setEditable(false);
-
-      SplashImage splashimg = new SplashImage(image);
-      iconimg.add(splashimg, BorderLayout.CENTER);
-      add(iconimg, BorderLayout.NORTH);
-    }
-    add(splashText, BorderLayout.CENTER);
-    splashText.addMouseListener(closer);
-    Desktop.desktop.add(iframe);
+    iframe.setLayer(JLayeredPane.PALETTE_LAYER);  
+    SplashImage splashimg = new SplashImage(image);
+    imgPanel.add(splashimg, BorderLayout.CENTER);
+    add(imgPanel, BorderLayout.NORTH);
+    Desktop.getDesktopPane().add(iframe);
     refreshText();
   }
 
   /**
-   * update text in author text panel reflecting current version information
+   * Both Java and JavaScript have to wait for images, but this method will
+   * accomplish nothing for JavaScript. We have already taken care of image
+   * loading with our state loop in JavaScript.
+   * 
    */
-  protected boolean refreshText()
+  private void waitForImages()
   {
-    String newtext = Desktop.instance.getAboutMessage();
-    // System.err.println("Text found: \n"+newtext+"\nEnd of newtext.");
-    if (oldTextLength != newtext.length())
+    if (Platform.isJS())
+      return;
+    MediaTracker mt = new MediaTracker(this);
+    try
     {
-      iframe.setVisible(false);
-      oldTextLength = newtext.length();
-      if (Platform.isJS()) // BH 2019
+      mt.addImage(image, 0);
+      logo = Toolkit.getDefaultToolkit().createImage(
+              getClass().getResource("/images/Jalview_Logo_small.png"));
+    } catch (Exception ex)
+    {
+    }
+    if (logo != null)
+    {
+      mt.addImage(logo, 1);
+    }
+    do
+    {
+      try
+      {
+        mt.waitForAll();
+      } catch (InterruptedException x)
       {
-        /*
-         * SwingJS doesn't have HTMLEditorKit, required for a JTextPane
-         * to display formatted html, so we use a simple alternative
-         */
-        String text = "<html><br><br><img src=\"swingjs/j2s/images/Jalview_Logo.png\"/><br>"
-                + newtext + "</html>";
-        JLabel ta = new JLabel(text);
-        ta.setOpaque(true);
-        ta.setBackground(Color.white);
-        splashText = ta;
       }
-      else
-      /**
-       * Java only
-       *
-       * @j2sIgnore
-       */
+      if (mt.isErrorAny())
       {
-        JTextPane jtp = new JTextPane();
-        jtp.setEditable(false);
-        jtp.setContentType("text/html");
-        jtp.setText("<html>" + newtext + "</html>");
-        jtp.addHyperlinkListener(this);
-        splashText = jtp;
+        System.err.println("Error when loading images!");
+        break;
       }
-      splashText.addMouseListener(closer);
-
-      splashText.setVisible(true);
-      splashText.setSize(new Dimension(750, 375));
-      add(splashText, BorderLayout.CENTER);
-      revalidate();
-      iframe.setBounds((Desktop.instance.getWidth() - 750) / 2,
-              (Desktop.instance.getHeight() - 375) / 2, 750,
-              splashText.getHeight() + iconimg.getHeight());
-      iframe.validate();
-      iframe.setVisible(true);
-      return true;
+    } while (!mt.checkAll());
+    if (logo != null)
+    {
+      Desktop.getInstance().setIconImage(logo);
     }
-    return false;
   }
 
   /**
-   * Create splash screen, display it and clear it off again.
+   * update text in author text panel reflecting current version information
    */
-  @Override
-  public void run()
+  protected boolean refreshText()
   {
-    initSplashScreenWindow();
-
-    long startTime = System.currentTimeMillis() / 1000;
-
-    while (visible)
+    String newtext = Desktop.getInstance().getAboutMessage();
+    if (oldTextLength == newtext.length())
     {
-      iframe.repaint();
-      try
-      {
-        Thread.sleep(500);
-      } catch (Exception ex)
-      {
-      }
-
-      if (transientDialog
-              && ((System.currentTimeMillis() / 1000) - startTime) > SHOW_FOR_SECS)
-      {
-        visible = false;
-      }
-
-      if (visible && refreshText())
-      {
-        iframe.repaint();
-      }
-      if (!transientDialog)
-      {
-        return;
-      }
+      return false;
     }
-
-    closeSplash();
-    Desktop.instance.startDialogQueue();
+    oldTextLength = newtext.length();
+    iframe.setVisible(false);
+    JTextPane jtp = new JTextPane();
+    jtp.setEditable(false);
+    jtp.setContentType("text/html");
+    jtp.setText("<html>" + newtext + "</html>");
+    jtp.addHyperlinkListener(this);
+    jtp.setFont(new Font("Verdana", Font.PLAIN, FONT_SIZE));
+    jtp.addMouseListener(closer);
+    jtp.setVisible(true);
+    jtp.setSize(new Dimension(750, 425));
+    add(jtp, BorderLayout.CENTER);
+    revalidate();
+    int h = jtp.getHeight() + imgPanel.getHeight();
+    iframe.setBounds(Math.max(0, (iframe.getParent().getWidth() - 750) / 2),
+           Math.max(0,  (iframe.getParent().getHeight() - h)/2), 750, h);
+    iframe.validate();
+    iframe.setVisible(true);
+    return true;
   }
 
-  /**
-   * DOCUMENT ME!
-   */
-  public void closeSplash()
+  protected void closeSplash()
   {
     try
     {
@@ -303,7 +215,47 @@ public class SplashScreen extends JPanel
     }
   }
 
-  public class SplashImage extends JPanel
+  /**
+   * A simple state machine with just three states: init, loop, and done. Ideal
+   * for a simple while/sleep loop that works in Java and JavaScript
+   * identically.
+   * 
+   */
+  @Override
+  public boolean stateLoop()
+  {
+    while (true)
+    {
+      switch (helper.getState())
+      {
+      case STATE_INIT:
+        initSplashScreenWindow();
+        helper.setState(STATE_LOOP);
+        continue;
+      case STATE_LOOP:
+        if (!isVisible())
+        {
+          helper.setState(STATE_DONE);
+          continue;
+        }
+        if (refreshText())
+        {
+          iframe.repaint();
+        }
+        if (isStartup)
+          helper.delayedState(SHOW_FOR_SECS * 1000, STATE_DONE);
+        return true;
+      default:
+      case STATE_DONE:
+        setVisible(false);
+        closeSplash();
+        Desktop.getInstance().startDialogQueue();
+        return true;
+      }
+    }
+  }
+
+  private class SplashImage extends JPanel
   {
     Image image;
 
@@ -329,7 +281,6 @@ public class SplashScreen extends JPanel
       g.setColor(Color.white);
       g.fillRect(0, 0, getWidth(), getHeight());
       g.setColor(Color.black);
-      g.setFont(new Font("Verdana", Font.BOLD, FONT_SIZE + 6));
 
       if (image != null)
       {
index 81f0134..c4b47a0 100644 (file)
@@ -153,7 +153,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     // allow about 65 pixels for Desktop decorators on Windows
 
     int newHeight = Math.min(height,
-            Desktop.instance.getHeight() - DESKTOP_DECORATORS_HEIGHT);
+            Desktop.getInstance().getHeight() - DESKTOP_DECORATORS_HEIGHT);
     if (newHeight != height)
     {
       int oldDividerLocation = getDividerLocation();
@@ -171,7 +171,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     // TODO if CommandListener is only ever 1:1 for complementary views,
     // may change broadcast pattern to direct messaging (more efficient)
     final StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.addCommandListener(((AlignFrame) getTopFrame()).getViewport());
     ssm.addCommandListener(((AlignFrame) getBottomFrame()).getViewport());
   }
@@ -428,7 +428,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-W / Cmd-W - close view or window
      */
     KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     action = new AbstractAction()
     {
       @Override
@@ -449,7 +449,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-T / Cmd-T open new view
      */
     KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     AbstractAction action = new AbstractAction()
     {
       @Override
@@ -572,7 +572,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     adjustLayout();
 
     final StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.addCommandListener(newTopPanel.av);
     ssm.addCommandListener(newBottomPanel.av);
   }
@@ -699,7 +699,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
    */
   protected void expandViews_actionPerformed()
   {
-    Desktop.instance.explodeViews(this);
+    Desktop.getInstance().explodeViews(this);
   }
 
   /**
@@ -708,7 +708,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
    */
   protected void gatherViews_actionPerformed()
   {
-    Desktop.instance.gatherViews(this);
+    Desktop.getInstance().gatherViews(this);
   }
 
   /**
@@ -805,7 +805,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-F / Cmd-F open Finder dialog, 'focused' on the right alignment
      */
     KeyStroke key_cmdF = KeyStroke.getKeyStroke(KeyEvent.VK_F,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     AbstractAction action = new AbstractAction()
     {
       @Override
index 3807ebd..97b0cfb 100644 (file)
 
 package jalview.gui;
 
-import jalview.api.structures.JalviewStructureDisplayI;
-import jalview.bin.Cache;
-import jalview.bin.Jalview;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.fts.api.FTSData;
-import jalview.fts.api.FTSDataColumnI;
-import jalview.fts.api.FTSRestClientI;
-import jalview.fts.core.FTSRestRequest;
-import jalview.fts.core.FTSRestResponse;
-import jalview.fts.service.pdb.PDBFTSRestClient;
-import jalview.io.DataSourceType;
-import jalview.jbgui.GStructureChooser;
-import jalview.structure.StructureMapping;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.MessageManager;
-import jalview.ws.DBRefFetcher;
-import jalview.ws.sifts.SiftsSettings;
-
 import java.awt.event.ItemEvent;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -59,6 +38,23 @@ import javax.swing.JTable;
 import javax.swing.SwingUtilities;
 import javax.swing.table.AbstractTableModel;
 
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
+import jalview.bin.Jalview;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.io.DataSourceType;
+import jalview.jbgui.GStructureChooser;
+import jalview.util.MessageManager;
+
 /**
  * Provides the behaviors for the Structure chooser Panel
  * 
@@ -69,7 +65,7 @@ import javax.swing.table.AbstractTableModel;
 public class StructureChooser extends GStructureChooser
         implements IProgressIndicator
 {
-  private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
+  static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
 
   private static int MAX_QLENGTH = 7820;
 
@@ -91,7 +87,7 @@ public class StructureChooser extends GStructureChooser
 
   private boolean cachedPDBExists;
 
-  private static StructureViewer lastTargetedView = null;
+  static StructureViewer lastTargetedView = null;
 
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
@@ -148,7 +144,7 @@ public class StructureChooser extends GStructureChooser
    */
   private void discoverStructureViews()
   {
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
       targetView.removeAllItems();
       if (lastTargetedView != null && !lastTargetedView.isVisible())
@@ -156,7 +152,7 @@ public class StructureChooser extends GStructureChooser
         lastTargetedView = null;
       }
       int linkedViewsAt = 0;
-      for (StructureViewerBase view : Desktop.instance
+      for (StructureViewerBase view : Desktop.getInstance()
               .getStructureViewers(null, null))
       {
         StructureViewer viewHandler = (lastTargetedView != null
@@ -165,8 +161,7 @@ public class StructureChooser extends GStructureChooser
 
         if (view.isLinkedWith(ap))
         {
-          targetView.insertItemAt(viewHandler,
-                  linkedViewsAt++);
+          targetView.insertItemAt(viewHandler, linkedViewsAt++);
         }
         else
         {
@@ -322,7 +317,7 @@ public class StructureChooser extends GStructureChooser
     boolean isUniProtRefsFound = false;
     StringBuilder queryBuilder = new StringBuilder();
     Set<String> seqRefs = new LinkedHashSet<>();
-    
+
     /*
      * note PDBs as DBRefEntry so they are not duplicated in query
      */
@@ -348,7 +343,7 @@ public class StructureChooser extends GStructureChooser
     {
       for (int ib = 0, nb = refs.size(); ib < nb; ib++)
       {
-         DBRefEntry dbRef = refs.get(ib);
+        DBRefEntry dbRef = refs.get(ib);
         if (isValidSeqName(getDBRefId(dbRef))
                 && queryBuilder.length() < MAX_QLENGTH)
         {
@@ -577,7 +572,8 @@ public class StructureChooser extends GStructureChooser
   @Override
   protected void pdbFromFile_actionPerformed()
   {
-    // TODO: JAL-3048 not needed for Jalview-JS until JSmol dep and StructureChooser
+    // TODO: JAL-3048 not needed for Jalview-JS until JSmol dep and
+    // StructureChooser
     // works
     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
@@ -639,8 +635,8 @@ public class StructureChooser extends GStructureChooser
     if (cachedPDBExist)
     {
       FilterOption cachedOption = new FilterOption(
-              MessageManager.getString("label.cached_structures"),
-              "-", VIEWS_LOCAL_PDB, false);
+              MessageManager.getString("label.cached_structures"), "-",
+              VIEWS_LOCAL_PDB, false);
       cmb_filterOption.addItem(cachedOption);
       cmb_filterOption.setSelectedItem(cachedOption);
     }
@@ -861,7 +857,7 @@ public class StructureChooser extends GStructureChooser
     }
     return found;
   }
-  
+
   /**
    * Handles the 'New View' action
    */
@@ -889,10 +885,11 @@ public class StructureChooser extends GStructureChooser
   public void showStructures(boolean waitUntilFinished)
   {
 
-    final StructureSelectionManager ssm = ap.getStructureSelectionManager();
-
     final int preferredHeight = pnl_filter.getHeight();
 
+    final StructureViewer theViewer = getTargetedStructureViewer();
+    boolean superimpose = chk_superpose.isSelected();
+
     Runnable viewStruc = new Runnable()
     {
       @Override
@@ -906,8 +903,7 @@ public class StructureChooser extends GStructureChooser
 
         if (currentView == VIEWS_FILTER)
         {
-          int pdbIdColIndex = restable.getColumn("PDB Id")
-                  .getModelIndex();
+          int pdbIdColIndex = restable.getColumn("PDB Id").getModelIndex();
           int refSeqColIndex = restable.getColumn("Ref Sequence")
                   .getModelIndex();
           int[] selectedRows = restable.getSelectedRows();
@@ -916,10 +912,10 @@ public class StructureChooser extends GStructureChooser
           List<SequenceI> selectedSeqsToView = new ArrayList<>();
           for (int row : selectedRows)
           {
-            String pdbIdStr = restable
-                    .getValueAt(row, pdbIdColIndex).toString();
-            SequenceI selectedSeq = (SequenceI) restable
-                    .getValueAt(row, refSeqColIndex);
+            String pdbIdStr = restable.getValueAt(row, pdbIdColIndex)
+                    .toString();
+            SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
+                    refSeqColIndex);
             selectedSeqsToView.add(selectedSeq);
             PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
             if (pdbEntry == null)
@@ -930,17 +926,15 @@ public class StructureChooser extends GStructureChooser
 
             if (pdbEntry == null)
             {
-              pdbEntry = new PDBEntry();
-              pdbEntry.setId(pdbIdStr);
-              pdbEntry.setType(PDBEntry.Type.PDB);
+              pdbEntry = new PDBEntry(pdbIdStr, null, "pdb");
               selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
             }
             pdbEntriesToView[count++] = pdbEntry;
           }
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
-          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
-                  selectedSeqs);
+          sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
+                  selectedSeqs, superimpose, theViewer, progressBar);
         }
         else if (currentView == VIEWS_LOCAL_PDB)
         {
@@ -963,8 +957,8 @@ public class StructureChooser extends GStructureChooser
           }
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
-          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
-                  selectedSeqs);
+          sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
+                  selectedSeqs, superimpose, theViewer, progressBar);
         }
         else if (currentView == VIEWS_ENTER_ID)
         {
@@ -993,9 +987,10 @@ public class StructureChooser extends GStructureChooser
           }
 
           PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
-          sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
+          sViewer = StructureViewer.launchStructureViewer(ap, pdbEntriesToView,
                   new SequenceI[]
-                  { selectedSequence });
+                  { selectedSequence }, superimpose, theViewer,
+                  progressBar);
         }
         else if (currentView == VIEWS_FROM_FILE)
         {
@@ -1005,16 +1000,12 @@ public class StructureChooser extends GStructureChooser
           {
             selectedSequence = userSelectedSeq;
           }
-          PDBEntry fileEntry = new AssociatePdbFileWithSeq()
-                  .associatePdbWithSeq(selectedPdbFileName,
-                          DataSourceType.FILE, selectedSequence, true,
-                          Desktop.instance);
-
-          sViewer = launchStructureViewer(
-                  ssm, new PDBEntry[]
-                  { fileEntry }, ap,
+          PDBEntry fileEntry = AssociatePdbFileWithSeq.associatePdbWithSeq(selectedPdbFileName,
+                          DataSourceType.FILE, selectedSequence, true);
+          sViewer = StructureViewer.launchStructureViewer(ap, new PDBEntry[] { fileEntry },
                   new SequenceI[]
-                  { selectedSequence });
+                  { selectedSequence }, superimpose, theViewer,
+                  progressBar);
         }
         SwingUtilities.invokeLater(new Runnable()
         {
@@ -1068,104 +1059,9 @@ public class StructureChooser extends GStructureChooser
    * @param ssm
    * @return
    */
-  StructureViewer getTargetedStructureViewer(
-          StructureSelectionManager ssm)
-  {
-    Object sv = targetView.getSelectedItem();
-
-    return sv == null ? new StructureViewer(ssm) : (StructureViewer) sv;
-  }
-
-  /**
-   * Adds PDB structures to a new or existing structure viewer
-   * 
-   * @param ssm
-   * @param pdbEntriesToView
-   * @param alignPanel
-   * @param sequences
-   * @return
-   */
-  private StructureViewer launchStructureViewer(
-          StructureSelectionManager ssm,
-          final PDBEntry[] pdbEntriesToView,
-          final AlignmentPanel alignPanel, SequenceI[] sequences)
+  StructureViewer getTargetedStructureViewer()
   {
-    long progressId = sequences.hashCode();
-    setProgressBar(MessageManager
-            .getString("status.launching_3d_structure_viewer"), progressId);
-    final StructureViewer theViewer = getTargetedStructureViewer(ssm);
-    boolean superimpose = chk_superpose.isSelected();
-    theViewer.setSuperpose(superimpose);
-
-    /*
-     * remember user's choice of superimpose or not
-     */
-    Cache.setProperty(AUTOSUPERIMPOSE,
-            Boolean.valueOf(superimpose).toString());
-
-    setProgressBar(null, progressId);
-    if (SiftsSettings.isMapWithSifts())
-    {
-      List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
-      int p = 0;
-      // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
-      // real PDB ID. For moment, we can also safely do this if there is already
-      // a known mapping between the PDBEntry and the sequence.
-      for (SequenceI seq : sequences)
-      {
-        PDBEntry pdbe = pdbEntriesToView[p++];
-        if (pdbe != null && pdbe.getFile() != null)
-        {
-          StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
-          if (smm != null && smm.length > 0)
-          {
-            for (StructureMapping sm : smm)
-            {
-              if (sm.getSequence() == seq)
-              {
-                continue;
-              }
-            }
-          }
-        }
-        if (seq.getPrimaryDBRefs().isEmpty())
-        {
-          seqsWithoutSourceDBRef.add(seq);
-          continue;
-        }
-      }
-      if (!seqsWithoutSourceDBRef.isEmpty())
-      {
-        int y = seqsWithoutSourceDBRef.size();
-        setProgressBar(MessageManager.formatMessage(
-                "status.fetching_dbrefs_for_sequences_without_valid_refs",
-                y), progressId);
-        SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
-                .toArray(new SequenceI[y]);
-        DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
-        dbRefFetcher.fetchDBRefs(true);
-
-        setProgressBar("Fetch complete.", progressId); // todo i18n
-      }
-    }
-    if (pdbEntriesToView.length > 1)
-    {
-      setProgressBar(MessageManager.getString(
-              "status.fetching_3d_structures_for_selected_entries"),
-              progressId);
-      theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel);
-    }
-    else
-    {
-      setProgressBar(MessageManager.formatMessage(
-              "status.fetching_3d_structures_for",
-              pdbEntriesToView[0].getId()),progressId);
-      theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
-    }
-    setProgressBar(null, progressId);
-    // remember the last viewer we used...
-    lastTargetedView = theViewer;
-    return theViewer;
+    return (StructureViewer) targetView.getSelectedItem();
   }
 
   /**
@@ -1204,7 +1100,8 @@ public class StructureChooser extends GStructureChooser
             && !discoveredStructuresSet.isEmpty();
   }
 
-  protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes this. 
+  protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes
+                               // this.
   // Doing a search for "1" or "1c" is valuable?
   // Those work but are enormously slow.
 
@@ -1212,51 +1109,51 @@ public class StructureChooser extends GStructureChooser
   protected void txt_search_ActionPerformed()
   {
     String text = txt_search.getText().trim();
-       if (text.length() >= PDB_ID_MIN) 
-    new Thread()
-    {
-
-       @Override
-      public void run()
+    if (text.length() >= PDB_ID_MIN)
+      new Thread()
       {
-        errorWarning.setLength(0);
-        isValidPBDEntry = false;
-        if (text.length() > 0)
+
+        @Override
+        public void run()
         {
-          String searchTerm = text.toLowerCase();
-          searchTerm = searchTerm.split(":")[0];
-          // System.out.println(">>>>> search term : " + searchTerm);
-          List<FTSDataColumnI> wantedFields = new ArrayList<>();
-          FTSRestRequest pdbRequest = new FTSRestRequest();
-          pdbRequest.setAllowEmptySeq(false);
-          pdbRequest.setResponseSize(1);
-          pdbRequest.setFieldToSearchBy("(pdb_id:");
-          pdbRequest.setWantedFields(wantedFields);
-          pdbRequest.setSearchTerm(searchTerm + ")");
-          pdbRequest.setAssociatedSequence(selectedSequence);
-          pdbRestClient = PDBFTSRestClient.getInstance();
-          wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
-          FTSRestResponse resultList;
-          try
-          {
-            resultList = pdbRestClient.executeRequest(pdbRequest);
-          } catch (Exception e)
-          {
-            errorWarning.append(e.getMessage());
-            return;
-          } finally
-          {
-            validateSelections();
-          }
-          if (resultList.getSearchSummary() != null
-                  && resultList.getSearchSummary().size() > 0)
+          errorWarning.setLength(0);
+          isValidPBDEntry = false;
+          if (text.length() > 0)
           {
-            isValidPBDEntry = true;
+            String searchTerm = text.toLowerCase();
+            searchTerm = searchTerm.split(":")[0];
+            // System.out.println(">>>>> search term : " + searchTerm);
+            List<FTSDataColumnI> wantedFields = new ArrayList<>();
+            FTSRestRequest pdbRequest = new FTSRestRequest();
+            pdbRequest.setAllowEmptySeq(false);
+            pdbRequest.setResponseSize(1);
+            pdbRequest.setFieldToSearchBy("(pdb_id:");
+            pdbRequest.setWantedFields(wantedFields);
+            pdbRequest.setSearchTerm(searchTerm + ")");
+            pdbRequest.setAssociatedSequence(selectedSequence);
+            pdbRestClient = PDBFTSRestClient.getInstance();
+            wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
+            FTSRestResponse resultList;
+            try
+            {
+              resultList = pdbRestClient.executeRequest(pdbRequest);
+            } catch (Exception e)
+            {
+              errorWarning.append(e.getMessage());
+              return;
+            } finally
+            {
+              validateSelections();
+            }
+            if (resultList.getSearchSummary() != null
+                    && resultList.getSearchSummary().size() > 0)
+            {
+              isValidPBDEntry = true;
+            }
           }
+          validateSelections();
         }
-        validateSelections();
-      }
-    }.start();
+      }.start();
   }
 
   @Override
@@ -1409,4 +1306,5 @@ public class StructureChooser extends GStructureChooser
   {
     return sViewer == null ? null : sViewer.sview;
   }
+
 }
index 0c8354b..e7a30f9 100644 (file)
@@ -25,7 +25,12 @@ import jalview.bin.Cache;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.StructureViewerModel;
+import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.sifts.SiftsSettings;
 
 import java.awt.Rectangle;
 import java.util.ArrayList;
@@ -45,6 +50,16 @@ import java.util.Map.Entry;
  */
 public class StructureViewer
 {
+  
+  static
+  {
+    Platform.loadStaticResource("core/core_jvjmol.z.js",
+            "org.jmol.viewer.Viewer");
+  }
+
+
+  
+  
   private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type ";
 
   StructureSelectionManager ssm;
@@ -388,4 +403,119 @@ public class StructureViewer
     superposeAdded = alignAddedStructures;
   }
 
+  /**
+   * Launch a minimal implementation of a StructureViewer.
+   * 
+   * @param alignPanel
+   * @param pdb
+   * @param seqs
+   * @return
+   */
+  public static StructureViewer launchStructureViewer(
+          AlignmentPanel alignPanel, PDBEntry pdb, SequenceI[] seqs)
+  {
+    return launchStructureViewer(alignPanel, new PDBEntry[] { pdb }, seqs,
+            false, null, null);
+  }
+
+  /**
+   * Adds PDB structures to a new or existing structure viewer
+   * 
+   * @param ssm
+   * @param pdbEntriesToView
+   * @param alignPanel
+   * @param sequences
+   * @return
+   */
+  protected static StructureViewer launchStructureViewer(
+          final AlignmentPanel ap, final PDBEntry[] pdbEntriesToView,
+          SequenceI[] sequences, boolean superimpose,
+          StructureViewer theViewer, IProgressIndicator pb)
+  {
+    final StructureSelectionManager ssm = ap.getStructureSelectionManager();
+    if (theViewer == null)
+      theViewer = new StructureViewer(ssm);
+    long progressId = sequences.hashCode();
+    if (pb != null)
+      pb.setProgressBar(MessageManager.getString(
+              "status.launching_3d_structure_viewer"), progressId);
+    theViewer.setSuperpose(superimpose);
+  
+    /*
+     * remember user's choice of superimpose or not
+     */
+    Cache.setProperty(StructureChooser.AUTOSUPERIMPOSE,
+            Boolean.valueOf(superimpose).toString());
+  
+    if (pb != null)
+      pb.setProgressBar(null, progressId);
+    if (SiftsSettings.isMapWithSifts())
+    {
+      List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
+      int p = 0;
+      // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
+      // real PDB ID. For moment, we can also safely do this if there is already
+      // a known mapping between the PDBEntry and the sequence.
+      for (SequenceI seq : sequences)
+      {
+        PDBEntry pdbe = pdbEntriesToView[p++];
+        if (pdbe != null && pdbe.getFile() != null)
+        {
+          StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
+          if (smm != null && smm.length > 0)
+          {
+            for (StructureMapping sm : smm)
+            {
+              if (sm.getSequence() == seq)
+              {
+                continue;
+              }
+            }
+          }
+        }
+        if (seq.getPrimaryDBRefs().isEmpty())
+        {
+          seqsWithoutSourceDBRef.add(seq);
+          continue;
+        }
+      }
+      if (!seqsWithoutSourceDBRef.isEmpty())
+      {
+        int y = seqsWithoutSourceDBRef.size();
+        if (pb != null)
+          pb.setProgressBar(MessageManager.formatMessage(
+                  "status.fetching_dbrefs_for_sequences_without_valid_refs",
+                  y), progressId);
+        SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+                .toArray(new SequenceI[y]);
+        DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
+        dbRefFetcher.fetchDBRefs(true);
+  
+        if (pb != null)
+          pb.setProgressBar("Fetch complete.", progressId); // todo i18n
+      }
+    }
+    if (pdbEntriesToView.length > 1)
+    {
+      if (pb != null)
+        pb.setProgressBar(MessageManager.getString(
+                "status.fetching_3d_structures_for_selected_entries"),
+                progressId);
+      theViewer.viewStructures(pdbEntriesToView, sequences, ap);
+    }
+    else
+    {
+      if (pb != null)
+        pb.setProgressBar(MessageManager.formatMessage(
+                "status.fetching_3d_structures_for",
+                pdbEntriesToView[0].getId()), progressId);
+      theViewer.viewStructures(pdbEntriesToView[0], sequences, ap);
+    }
+    if (pb != null)
+      pb.setProgressBar(null, progressId);
+    // remember the last viewer we used...
+    StructureChooser.lastTargetedView = theViewer;
+    return theViewer;
+  }
+
 }
index 418a84d..af48093 100644 (file)
@@ -407,7 +407,7 @@ public abstract class StructureViewerBase extends GStructureViewer
    */
   protected List<StructureViewerBase> getViewersFor(AlignmentPanel alp)
   {
-    return Desktop.instance.getStructureViewers(alp, this.getClass());
+    return Desktop.getInstance().getStructureViewers(alp, this.getClass());
   }
 
   @Override
index 29ba52b..e59dfcc 100755 (executable)
@@ -761,7 +761,9 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
      */
     if (e.isPopupTrigger())
     {
-      chooseSubtreeColour();
+      if (highlightNode != null) {
+        chooseSubtreeColour();
+      }
       e.consume(); // prevent mouseClicked happening
     }
   }
index 37b4dac..d242892 100755 (executable)
@@ -174,11 +174,12 @@ public class TreePanel extends GTreePanel
   {
     final PropertyChangeListener listener = new PropertyChangeListener()
     {
+      @SuppressWarnings("unchecked")
       @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
-        if (evt.getPropertyName().equals("alignment"))
-        {
+        switch (evt.getPropertyName()) {
+        case AlignmentViewport.PROPERTY_ALIGNMENT:
           if (tree == null)
           {
             System.out.println("tree is null");
@@ -191,12 +192,14 @@ public class TreePanel extends GTreePanel
           {
             System.out.println(
                     "new alignment sequences vector value is null");
+            return;
           }
 
           tree.updatePlaceHolders((List<SequenceI>) evt.getNewValue());
           treeCanvas.nameHash.clear(); // reset the mapping between canvas
           // rectangles and leafnodes
           repaint();
+          break;
         }
       }
     };
index 4846049..1139ae0 100755 (executable)
@@ -149,8 +149,8 @@ public class UserDefinedColours extends GUserDefinedColours
     frame = new JInternalFrame();
     frame.setContentPane(this);
     Desktop.addInternalFrame(frame,
-            MessageManager.getString("label.user_defined_colours"),
-            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
+            MessageManager.getString("label.user_defined_colours"), Desktop.FRAME_MAKE_VISIBLE,
+            MY_FRAME_WIDTH, MY_FRAME_HEIGHT, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
   }
 
   /**
@@ -448,7 +448,7 @@ public class UserDefinedColours extends GUserDefinedColours
   {
     if (isNoSelectionMade())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.no_colour_selection_in_scheme"),
               MessageManager.getString("label.no_colour_selection_warn"),
@@ -503,7 +503,7 @@ public class UserDefinedColours extends GUserDefinedColours
         String[] options = new String[] { title,
             MessageManager.getString("label.dont_save_changes"), };
         final String question = JvSwingUtils.wrapTooltip(true, message);
-        int response = JvOptionPane.showOptionDialog(Desktop.desktop,
+        int response = JvOptionPane.showOptionDialog(Desktop.getDesktopPane(),
                 question, title, JvOptionPane.DEFAULT_OPTION,
                 JvOptionPane.PLAIN_MESSAGE, null, options, options[0]);
 
@@ -557,7 +557,7 @@ public class UserDefinedColours extends GUserDefinedColours
   {
     if (isNoSelectionMade())
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.no_colour_selection_in_scheme"),
               MessageManager.getString("label.no_colour_selection_warn"),
@@ -741,7 +741,7 @@ public class UserDefinedColours extends GUserDefinedColours
     String name = schemeName.getText().trim();
     if (name.length() < 1)
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+      JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
               MessageManager
                       .getString("label.user_colour_scheme_must_have_name"),
               MessageManager.getString("label.no_name_colour_scheme"),
@@ -756,7 +756,7 @@ public class UserDefinedColours extends GUserDefinedColours
        * @j2sIgnore
        */
       {
-        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
+        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(),
                 MessageManager.formatMessage(
                         "label.colour_scheme_exists_overwrite", new Object[]
                         { name, name }),
index 8c3c76e..2f6a5c2 100644 (file)
  */
 package jalview.gui;
 
-import jalview.util.MessageManager;
-
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
 import java.net.URL;
 
+import jalview.util.MessageManager;
+
 public class UserQuestionnaireCheck implements Runnable
 {
   /**
@@ -139,7 +139,7 @@ public class UserQuestionnaireCheck implements Runnable
                 + qid + "&rid=" + rid;
         jalview.bin.Cache.log
                 .info("Prompting user for questionnaire at " + qurl);
-        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
+        int reply = JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.jalview_new_questionnaire"),
                 MessageManager.getString("label.jalview_user_survey"),
                 JvOptionPane.YES_NO_OPTION, JvOptionPane.QUESTION_MESSAGE);
index 0848a4d..2ee3e06 100644 (file)
@@ -173,7 +173,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
           }
         } catch (InvalidSessionDocumentException e)
         {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+          JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
 
                   MessageManager.getString(
                           "label.vamsas_doc_couldnt_be_opened_as_new_session"),
@@ -455,7 +455,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
     VamsasAppDatastore vds = new VamsasAppDatastore(doc, vobj2jv, jv2vobj,
             baseProvEntry(), alRedoState);
     // wander through frames
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
 
     if (frames == null)
     {
@@ -660,7 +660,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                   Cache.log.debug(
                           "Asking user if the vamsas session should be stored.");
                   int reply = JvOptionPane.showInternalConfirmDialog(
-                          Desktop.desktop,
+                          Desktop.getDesktopPane(),
                           "The current VAMSAS session has unsaved data - do you want to save it ?",
                           "VAMSAS Session Shutdown",
                           JvOptionPane.YES_NO_OPTION,
@@ -669,7 +669,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                   if (reply == JvOptionPane.YES_OPTION)
                   {
                     Cache.log.debug("Prompting for vamsas store filename.");
-                    Desktop.instance.vamsasSave_actionPerformed(null);
+                    Desktop.getInstance().vamsasSave_actionPerformed(null);
                     Cache.log
                             .debug("Finished attempt at storing document.");
                   }
@@ -689,7 +689,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
   public void disableGui(boolean b)
   {
     // JAL-3311 TODO: remove this class!
-    // Desktop.instance.setVamsasUpdate(b);
+    // Desktop.getInstance().setVamsasUpdate(b);
   }
 
   Hashtable _backup_vobj2jv;
@@ -764,7 +764,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
       {
         final IPickManager pm = vclient.getPickManager();
         final StructureSelectionManager ssm = StructureSelectionManager
-                .getStructureSelectionManager(Desktop.instance);
+                .getStructureSelectionManager(Desktop.getInstance());
         final VamsasApplication me = this;
         pm.registerMessageHandler(new IMessageHandler()
         {
index 03d76ee..b88102e 100644 (file)
@@ -259,6 +259,7 @@ public class WebserviceInfo extends GWebserviceInfo
   public WebserviceInfo(String title, String info, int width, int height,
           boolean makeVisible)
   {
+    // no references
     init(title, info, width, height, makeVisible);
   }
 
@@ -322,7 +323,7 @@ public class WebserviceInfo extends GWebserviceInfo
   {
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    Desktop.addInternalFrame(frame, title, makeVisible, width, height);
+    Desktop.addInternalFrame(frame, title, makeVisible, width, height, Desktop.FRAME_ALLOW_RESIZE, Desktop.FRAME_SET_MIN_SIZE_300);
     frame.setClosable(false);
 
     progressBar = new ProgressBar(statusPanel, statusBar);
@@ -746,7 +747,7 @@ public class WebserviceInfo extends GWebserviceInfo
       @Override
       public void run()
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop, message,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), message,
                 title, JvOptionPane.WARNING_MESSAGE);
 
       }
index d359135..719b7d9 100644 (file)
@@ -198,15 +198,17 @@ public class WsJobParameters extends JPanel implements ItemListener,
   public boolean showRunDialog()
   {
 
-    frame = new JDialog(Desktop.instance, true);
+    frame = new JDialog(Desktop.getInstance(), true);
     if (service != null)
     {
       frame.setTitle(MessageManager.formatMessage("label.edit_params_for",
               new String[]
       { service.getActionText() }));
     }
-
-    Rectangle deskr = Desktop.instance.getBounds();
+    frame.setTitle(MessageManager.formatMessage("label.edit_params_for",
+            new String[]
+            { service.getActionText() }));
+    Rectangle deskr = Desktop.getInstance().getBounds();
     Dimension pref = this.getPreferredSize();
     frame.setBounds(
             new Rectangle((int) (deskr.getCenterX() - pref.width / 2),
@@ -415,9 +417,9 @@ public class WsJobParameters extends JPanel implements ItemListener,
     dialogpanel.add(canceljob);
     // JAL-1580: setMaximumSize() doesn't work, so just size for the worst case:
     // check for null is for JUnit usage
-    final int windowHeight = Desktop.instance == null ? DEFAULT_HEIGHT
-            : Desktop.instance.getHeight();
-    // setPreferredSize(new Dimension(PREFERRED_WIDTH, windowHeight));
+    final int windowHeight = Desktop.getInstance() == null ? DEFAULT_HEIGHT
+            : Desktop.getInstance().getHeight();
+    setPreferredSize(new Dimension(540, windowHeight));
     add(dialogpanel, BorderLayout.SOUTH);
     validate();
   }
@@ -1038,7 +1040,7 @@ public class WsJobParameters extends JPanel implements ItemListener,
    */
   protected void updateWebServiceMenus()
   {
-    if (Desktop.instance == null)
+    if (Desktop.getInstance() == null)
     {
       return;
     }
index c46fd3a..272e05b 100644 (file)
@@ -205,7 +205,7 @@ public class WsParamSetManager implements ParamManager
       chooser.setDialogTitle(MessageManager
               .getString("label.choose_filename_for_param_file"));
       chooser.setToolTipText(MessageManager.getString("action.save"));
-      int value = chooser.showSaveDialog(Desktop.instance);
+      int value = chooser.showSaveDialog(Desktop.getInstance());
       if (value == JalviewFileChooser.APPROVE_OPTION)
       {
         outfile = chooser.getSelectedFile();
@@ -314,7 +314,7 @@ public class WsParamSetManager implements ParamManager
       File pfile = new File(filename);
       if (pfile.exists() && pfile.canWrite())
       {
-        if (JvOptionPane.showConfirmDialog(Desktop.instance,
+        if (JvOptionPane.showConfirmDialog(Desktop.getInstance(),
                 "Delete the preset's file, too ?", "Delete User Preset ?",
                 JvOptionPane.OK_CANCEL_OPTION) == JvOptionPane.OK_OPTION)
         {
index 51b40dd..cf88718 100644 (file)
  */
 package jalview.gui;
 
-import jalview.bin.Cache;
-import jalview.jbgui.GWsPreferences;
-import jalview.util.MessageManager;
-import jalview.ws.WSDiscovererI;
-import jalview.ws.jws2.Jws2Discoverer;
-import jalview.ws.rest.RestServiceDescription;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -45,6 +39,13 @@ import javax.swing.JTextField;
 import javax.swing.table.AbstractTableModel;
 import javax.swing.table.TableCellRenderer;
 
+import jalview.bin.Cache;
+import jalview.jbgui.GWsPreferences;
+import jalview.util.MessageManager;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.rest.RestServiceDescription;
+
 public class WsPreferences extends GWsPreferences
 {
 
@@ -66,7 +67,7 @@ public class WsPreferences extends GWsPreferences
   private void initFromPreferences()
   {
 
-    wsUrls = Jws2Discoverer.getDiscoverer().getServiceUrls();
+    wsUrls = Jws2Discoverer.getInstance().getServiceUrls();
     if (!wsUrls.isEmpty())
     {
       oldUrls = new Vector<String>(wsUrls);
@@ -123,7 +124,7 @@ public class WsPreferences extends GWsPreferences
     int r = 0;
     for (String url : wsUrls)
     {
-      int status = Jws2Discoverer.getDiscoverer().getServerStatusFor(url);
+      int status = Jws2Discoverer.getInstance().getServerStatusFor(url);
       tdat[r][1] = Integer.valueOf(status);
       tdat[r++][0] = url;
     }
@@ -243,7 +244,7 @@ public class WsPreferences extends GWsPreferences
 
   private void updateServiceList()
   {
-    Jws2Discoverer.getDiscoverer().setServiceUrls(wsUrls);
+    Jws2Discoverer.getInstance().setServiceUrls(wsUrls);
   }
 
   private void updateRsbsServiceList()
@@ -456,7 +457,7 @@ public class WsPreferences extends GWsPreferences
     boolean valid = false;
     int resp = JvOptionPane.CANCEL_OPTION;
     while (!valid && (resp = JvOptionPane.showInternalConfirmDialog(
-            Desktop.desktop, panel, title,
+            Desktop.getDesktopPane(), panel, title,
             JvOptionPane.OK_CANCEL_OPTION)) == JvOptionPane.OK_OPTION)
     {
       try
@@ -474,26 +475,26 @@ public class WsPreferences extends GWsPreferences
       } catch (Exception e)
       {
         valid = false;
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                 MessageManager.getString("label.invalid_url"));
       }
     }
     if (valid && resp == JvOptionPane.OK_OPTION)
     {
-      int validate = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
+      int validate = JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(),
               MessageManager.getString("info.validate_jabaws_server"),
               MessageManager.getString("label.test_server"),
               JvOptionPane.YES_NO_OPTION);
 
       if (validate == JvOptionPane.OK_OPTION)
       {
-        if (Jws2Discoverer.getDiscoverer().testServiceUrl(foo))
+        if (Jws2Discoverer.getInstance().testServiceUrl(foo))
         {
           return foo.toString();
         }
         else
         {
-          int opt = JvOptionPane.showInternalOptionDialog(Desktop.desktop,
+          int opt = JvOptionPane.showInternalOptionDialog(Desktop.getDesktopPane(),
                   "The Server  '" + foo.toString()
                           + "' failed validation,\ndo you want to add it anyway? ",
                   "Server Validation Failed", JvOptionPane.YES_NO_OPTION,
@@ -504,7 +505,7 @@ public class WsPreferences extends GWsPreferences
           }
           else
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     MessageManager.getString(
                             "warn.server_didnt_pass_validation"));
           }
@@ -597,7 +598,7 @@ public class WsPreferences extends GWsPreferences
           if (lastrefresh != update)
           {
             lastrefresh = update;
-            Desktop.instance.startServiceDiscovery(true); // wait around for all
+            Desktop.getInstance().startServiceDiscovery(true); // wait around for all
                                                           // threads to complete
             updateList();
 
@@ -618,15 +619,15 @@ public class WsPreferences extends GWsPreferences
         public void run()
         {
           long ct = System.currentTimeMillis();
-          Desktop.instance.setProgressBar(MessageManager
+          Desktop.getInstance().setProgressBar(MessageManager
                   .getString("status.refreshing_web_service_menus"), ct);
           if (lastrefresh != update)
           {
             lastrefresh = update;
-            Desktop.instance.startServiceDiscovery(true);
+            Desktop.getInstance().startServiceDiscovery(true);
             updateList();
           }
-          Desktop.instance.setProgressBar(null, ct);
+          Desktop.getInstance().setProgressBar(null, ct);
         }
 
       }).start();
@@ -648,8 +649,8 @@ public class WsPreferences extends GWsPreferences
   @Override
   protected void resetWs_actionPerformed(ActionEvent e)
   {
-    Jws2Discoverer.getDiscoverer().setServiceUrls(null);
-    List<String> nwsUrls = Jws2Discoverer.getDiscoverer().getServiceUrls();
+    Jws2Discoverer.getInstance().setServiceUrls(null);
+    List<String> nwsUrls = Jws2Discoverer.getInstance().getServiceUrls();
     if (!wsUrls.equals(nwsUrls))
     {
       update++;
index a18d38d..7021fae 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.httpserver;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.rest.RestHandler;
 
 import java.net.BindException;
@@ -49,39 +51,8 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
  * @author gmcarstairs
  * @see http://eclipse.org/jetty/documentation/current/embedding-jetty.html
  */
-public class HttpServer
+public class HttpServer implements ApplicationSingletonI
 {
-  /*
-   * 'context root' - actually just prefixed to the path for each handler for
-   * now - see registerHandler
-   */
-  private static final String JALVIEW_PATH = "jalview";
-
-  /*
-   * Singleton instance of this server
-   */
-  private static HttpServer instance;
-
-  /*
-   * The Http server
-   */
-  private Server server;
-
-  /*
-   * Registered handlers for context paths
-   */
-  private HandlerCollection contextHandlers;
-
-  /*
-   * Lookup of ContextHandler by its wrapped handler
-   */
-  Map<Handler, ContextHandler> myHandlers = new HashMap<Handler, ContextHandler>();
-
-  /*
-   * The context root for the server
-   */
-  private URI contextRoot;
-
   /**
    * Returns the singleton instance of this class.
    * 
@@ -92,14 +63,10 @@ public class HttpServer
   {
     synchronized (HttpServer.class)
     {
-      if (instance == null)
-      {
-        instance = new HttpServer();
-      }
-      return instance;
+      return (HttpServer) ApplicationSingletonProvider.getInstance(HttpServer.class);
     }
   }
-
+  
   /**
    * Private constructor to enforce use of singleton
    * 
@@ -116,6 +83,34 @@ public class HttpServer
     registerHandler(RestHandler.getInstance());
   }
 
+
+  /*
+   * 'context root' - actually just prefixed to the path for each handler for
+   * now - see registerHandler
+   */
+  private static final String JALVIEW_PATH = "jalview";
+
+  /*
+   * The Http server
+   */
+  private Server server;
+
+  /*
+   * Registered handlers for context paths
+   */
+  private HandlerCollection contextHandlers;
+
+  /*
+   * Lookup of ContextHandler by its wrapped handler
+   */
+  Map<Handler, ContextHandler> myHandlers = new HashMap<Handler, ContextHandler>();
+
+  /*
+   * The context root for the server
+   */
+  private URI contextRoot;
+
+
   /**
    * Start the http server
    * 
index 4916bb3..f01bfdc 100755 (executable)
@@ -158,11 +158,7 @@ public class AppletFormatAdapter
   {
 
     this.selectedFile = selectedFile;
-    if (selectedFile != null)
-    {
-      this.inFile = selectedFile.getPath();
-    }
-    this.inFile = file;
+    inFile = (selectedFile == null ? file : selectedFile.getPath());
     try
     {
       if (fileFormat.isStructureFile())
diff --git a/src/jalview/io/BSMLFile.java b/src/jalview/io/BSMLFile.java
new file mode 100644 (file)
index 0000000..69e29f8
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview 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 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview 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 Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io;
+
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.MessageManager;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
+import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;
+import fr.orsay.lri.varna.exceptions.ExceptionPermissionDenied;
+
+/**
+ * Preliminary reader for Bioinformatics Sequence Markup Language
+ * http://www.bsml.org
+ * 
+ * @author hansonr
+ *
+ */
+public class BSMLFile extends AlignFile
+{
+
+  public BSMLFile()
+  {
+    super();
+
+  }
+
+  public BSMLFile(String inFile, DataSourceType type) throws IOException
+  {
+    super(inFile, type);
+
+  }
+
+  public BSMLFile(FileParse source) throws IOException
+  {
+    super(source);
+
+  }
+
+  public BufferedReader CreateReader() throws FileNotFoundException
+  {
+    FileReader fr = null;
+    fr = new FileReader(inFile);
+
+    BufferedReader r = new BufferedReader(fr);
+    return r;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.io.AlignFile#parse()
+   */
+  @Override
+  public void parse() throws IOException
+  {
+    try
+    {
+      _parse();
+    } catch (ExceptionPermissionDenied pdx)
+    {
+      errormessage = MessageManager.formatMessage(
+              "exception.BSML_couldnt_access_datasource", new String[]
+              { pdx.getMessage() });
+      throw new IOException(pdx);
+    } catch (ExceptionLoadingFailed lf)
+    {
+      errormessage = MessageManager.formatMessage(
+              "exception.BSML_couldnt_process_data", new String[]
+              { lf.getMessage() });
+      throw new IOException(lf);
+    } catch (ExceptionFileFormatOrSyntax iff)
+    {
+      errormessage = MessageManager
+              .formatMessage("exception.BSML_invalid_file", new String[]
+              { iff.getMessage() });
+      throw new IOException(iff);
+    } catch (Exception x)
+    {
+      error = true;
+      errormessage = MessageManager.formatMessage(
+              "exception.BSML_problem_parsing_data", new String[]
+              { x.getMessage() });
+      throw new IOException(errormessage, x);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public void _parse()
+          throws ExceptionPermissionDenied, ExceptionLoadingFailed,
+          ExceptionFileFormatOrSyntax, ParserConfigurationException,
+          SAXException, IOException
+  {
+
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+
+    dbf.setIgnoringElementContentWhitespace(true);
+    dbf.setIgnoringComments(true);
+    dbf.setValidating(true);
+    dbf.setCoalescing(true);
+    dbf.setNamespaceAware(true);
+      dbf.setFeature("http://xml.org/sax/features/namespaces", false);
+      dbf.setFeature("http://xml.org/sax/features/validation", false);
+      dbf.setFeature(
+              "http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
+              false);
+      dbf.setFeature(
+              "http://apache.org/xml/features/nonvalidating/load-external-dtd",
+              false);
+
+      DocumentBuilder db = dbf.newDocumentBuilder();
+
+      Map<String, SequenceI> htSeq = new Hashtable<>();
+      InputSource is = new InputSource(getReader());
+      Document d = db.parse(is);
+      NodeList sequences = d.getElementsByTagName("Sequence-data");
+      int n = sequences.getLength();
+      SequenceI[] sqs = new SequenceI[n];
+      for (int i = 0; i < n; i++)
+      {
+        Element e = (Element) sequences.item(i);
+        String s = e.getTextContent();
+        String id = e.getAttribute("seq-name");
+        SequenceI seq = sqs[i] = new Sequence(id, s, 1, s.length());
+        htSeq.put(id, seq);
+        // ?? sqs[i].setEnd(sqs[i].findPosition(sqs[i].getLength()));
+      }
+
+      sequences = d.getElementsByTagName("Sequence");
+      n = sequences.getLength();
+      for (int i = 0; i < n; i++)
+      {
+        Element e = (Element) sequences.item(i);
+        String mol = e.getAttribute("molecule"); // dna or rna
+        if (!"dna".equals(mol))
+        {
+          System.err.println("BSML molecule=rna not implemented");
+          continue;
+        }
+        String title = e.getAttribute("title");
+        SequenceI seq = htSeq.get(title);
+        if (seq == null)
+        {
+          continue;
+        }
+      NodeList features = e.getElementsByTagName("Feature");
+        int featureCount = features.getLength();
+        for (int f = 0; f < featureCount; f++)
+        {
+          Element feature = (Element) features.item(f);
+          // <Feature class="GENE" title="CPXV-GER_1980_EP4-211">
+          // <Interval-loc complement="0" endpos="217104" startpos="216643"/>
+          // <Resource id="GENE-ID:119705"/>
+          // </Feature>
+        Element iloc = (Element) feature
+                .getElementsByTagName("Interval-loc").item(0);
+          String complement = iloc.getAttribute("complement");
+          if (!"0".equals(complement))
+          {
+            // Jalview cannot handle complement genes (running backward on the
+            // complementary strand);
+            continue;
+          }
+          String fclass = feature.getAttribute("class");
+          if (!"GENE".equals(fclass))
+          {
+            // just processing GENE features for now;
+            continue;
+          }
+          String ftitle = feature.getAttribute("title");
+        int start = Integer.parseInt(iloc.getAttribute("startpos"));
+        int end = Integer.parseInt(iloc.getAttribute("endpos"));
+        SequenceFeature sf = new SequenceFeature("GENE", ftitle, start, end,
+                null);
+        seq.addSequenceFeature(sf);
+        }
+        setSeqs(sqs);
+      }
+
+  }
+
+  @Override
+  public String print(SequenceI[] s, boolean jvSuffix)
+  {
+    return "not yet implemented";
+  }
+
+}
index 0d5f92b..9537442 100644 (file)
@@ -538,7 +538,7 @@ public class BackupFiles
           MessageManager.getString("label.delete"),
           MessageManager.getString("label.rename") };
 
-      confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop,
+      confirmButton = JvOptionPane.showOptionDialog(Desktop.getDesktopPane(),
               messageSB.toString(),
               MessageManager.getString("label.backupfiles_confirm_delete"),
               JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
@@ -558,7 +558,7 @@ public class BackupFiles
           MessageManager.getString("label.delete"),
           MessageManager.getString("label.keep") };
 
-      confirmButton = JvOptionPane.showOptionDialog(Desktop.desktop,
+      confirmButton = JvOptionPane.showOptionDialog(Desktop.getDesktopPane(),
               messageSB.toString(),
               MessageManager.getString("label.backupfiles_confirm_delete"),
               JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
@@ -593,7 +593,7 @@ public class BackupFiles
                       Long.toString(df.length()) }));
         }
 
-        int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop,
+        int confirmButton = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
                 messageSB.toString(),
                 MessageManager
                         .getString("label.backupfiles_confirm_delete"),
@@ -682,7 +682,7 @@ public class BackupFiles
                 "label.backupfiles_confirm_save_new_saved_file_not_ok"));
       }
 
-      int confirmButton = JvOptionPane.showConfirmDialog(Desktop.desktop,
+      int confirmButton = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
               messageSB.toString(),
               MessageManager
                       .getString("label.backupfiles_confirm_save_file"),
index 9db3df2..5573e0d 100644 (file)
@@ -25,6 +25,7 @@ import jalview.gui.OOMWarning;
 import jalview.json.binding.biojs.BioJSReleasePojo;
 import jalview.json.binding.biojs.BioJSRepositoryPojo;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
@@ -44,8 +45,7 @@ public class BioJsHTMLOutput extends HTMLOutput
 
   private static TreeMap<String, File> bioJsMSAVersions;
 
-  public static final String DEFAULT_DIR = System.getProperty("user.home")
-          + File.separatorChar + ".biojs_templates" + File.separatorChar;
+  public static final String DEFAULT_DIR = Platform.getUserPath(".biojs_templates/");
 
   public static final String BJS_TEMPLATES_LOCAL_DIRECTORY = jalview.bin.Cache
           .getDefault("biojs_template_directory", DEFAULT_DIR);
index eea3dae..a691c53 100644 (file)
@@ -43,7 +43,7 @@ public class CountReader
     {
       if (!Jalview.isHeadlessMode())
       {
-        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+        JvOptionPane.showInternalMessageDialog(Desktop.getInstance(),
                 MessageManager.getString("label.couldnt_read_data") + " in "
                         + file + "\n"
                         + AppletFormatAdapter.getSupportedFormats(),
index 2ffe577..b7df731 100644 (file)
@@ -388,6 +388,20 @@ public enum FileFormat implements FileFormatI
     {
       return new HMMFile();
     }
+  },   BSML("BSML", "bbb", true, false)
+  {
+    @Override
+    public AlignmentFileReaderI getReader(FileParse source)
+            throws IOException
+    {
+      return new BSMLFile(source);
+    }
+
+    @Override
+    public AlignmentFileWriterI getWriter(AlignmentI al)
+    {
+      return null;
+    }
   };
 
 
index aadcdb9..fca29bd 100644 (file)
@@ -27,6 +27,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A singleton registry of alignment file formats known to Jalview. On startup,
  * the 'built-in' formats are added (from the FileFormat enum). Additional
@@ -36,23 +39,11 @@ import java.util.Set;
  * @author gmcarstairs
  *
  */
-public class FileFormats
+public class FileFormats implements ApplicationSingletonI
 {
-  private static FileFormats instance = new FileFormats();
-
-  /*
-   * A lookup map of file formats by upper-cased name
-   */
-  private static Map<String, FileFormatI> formats;
-
-  /*
-   * Formats in this set are capable of being identified by IdentifyFile 
-   */
-  private static Set<FileFormatI> identifiable;
-
   public static FileFormats getInstance()
   {
-    return instance;
+    return (FileFormats) ApplicationSingletonProvider.getInstance(FileFormats.class);
   }
 
   /**
@@ -63,6 +54,17 @@ public class FileFormats
     reset();
   }
 
+  /*
+   * A lookup map of file formats by upper-cased name
+   */
+  private Map<String, FileFormatI> formats;
+
+  /*
+   * Formats in this set are capable of being identified by IdentifyFile 
+   */
+  private Set<FileFormatI> identifiable;
+
+
   /**
    * Reset to just the built-in file formats packaged with Jalview. These are
    * added (and will be shown in menus) in the order of their declaration in the
index 31a4deb..df64702 100755 (executable)
@@ -20,6 +20,7 @@
  */
 package jalview.io;
 
+import java.awt.Dimension;
 import java.io.File;
 import java.io.IOException;
 import java.util.StringTokenizer;
@@ -46,6 +47,7 @@ import jalview.project.Jalview2XML;
 import jalview.schemes.ColourSchemeI;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.utils.UrlDownloadClient;
 
 import java.util.ArrayList;
@@ -301,9 +303,9 @@ public class FileLoader implements Runnable
     Runtime rt = Runtime.getRuntime();
     try
     {
-      if (Desktop.instance != null)
+      if (Desktop.getInstance() != null)
       {
-        Desktop.instance.startLoading(file);
+        Desktop.getInstance().startLoading(file);
       }
       if (format == null)
       {
@@ -325,12 +327,12 @@ public class FileLoader implements Runnable
 
       if (format == null)
       {
-        Desktop.instance.stopLoading();
+        Desktop.getInstance().stopLoading();
         System.err.println("The input file \"" + file
                 + "\" has null or unidentifiable data content!");
         if (!Jalview.isHeadlessMode())
         {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+          JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                   MessageManager.getString("label.couldnt_read_data")
                           + " in " + file + "\n"
                           + AppletFormatAdapter.getSupportedFormats(),
@@ -341,7 +343,7 @@ public class FileLoader implements Runnable
       }
       // TODO: cache any stream datasources as a temporary file (eg. PDBs
       // retrieved via URL)
-      if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
+      if (Desktop.getDesktopPane() != null && Desktop.getDesktopPane().isShowMemoryUsage())
       {
         System.gc();
         memused = (rt.maxMemory() - rt.totalMemory() + rt.freeMemory()); // free
@@ -434,7 +436,7 @@ public class FileLoader implements Runnable
                 // register PDB entries with desktop's structure selection
                 // manager
                 StructureSelectionManager
-                        .getStructureSelectionManager(Desktop.instance)
+                        .getStructureSelectionManager(Desktop.getInstance())
                         .registerPDBEntry(pdbe);
               }
             }
@@ -505,8 +507,7 @@ public class FileLoader implements Runnable
             // add metadata and update ui
             if (!(protocol == DataSourceType.PASTE))
             {
-              alignFrame.setFileName(file, format);
-              alignFrame.setFileObject(selectedFile); // BH 2018 SwingJS
+              alignFrame.setFile(file, selectedFile, protocol, format);
             }
             if (proxyColourScheme != null)
             {
@@ -524,8 +525,12 @@ public class FileLoader implements Runnable
               // status in Jalview 3
               // TODO: define 'virtual desktop' for benefit of headless scripts
               // that perform queries to find the 'current working alignment'
-              Desktop.addInternalFrame(alignFrame, title,
+              
+              Dimension dim = Platform.getDimIfEmbedded(alignFrame,
                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+              alignFrame.setSize(dim);
+              Desktop.addInternalFrame(alignFrame, title, dim.width,
+                      dim.height);
             }
 
             try
@@ -539,23 +544,23 @@ public class FileLoader implements Runnable
         }
         else
         {
-          if (Desktop.instance != null)
+          if (Desktop.getInstance() != null)
           {
-            Desktop.instance.stopLoading();
+            Desktop.getInstance().stopLoading();
           }
 
           final String errorMessage = MessageManager.getString(
                   "label.couldnt_load_file") + " " + title + "\n" + error;
           // TODO: refactor FileLoader to be independent of Desktop / Applet GUI
           // bits ?
-          if (raiseGUI && Desktop.desktop != null)
+          if (raiseGUI && Desktop.getDesktopPane() != null)
           {
             javax.swing.SwingUtilities.invokeLater(new Runnable()
             {
               @Override
               public void run()
               {
-                JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+                JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                         errorMessage,
                         MessageManager
                                 .getString("label.error_loading_file"),
@@ -588,7 +593,7 @@ public class FileLoader implements Runnable
           @Override
           public void run()
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     MessageManager.formatMessage(
                             "label.problems_opening_file", new String[]
                             { file }),
@@ -610,7 +615,7 @@ public class FileLoader implements Runnable
           @Override
           public void run()
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     MessageManager.formatMessage(
                             "warn.out_of_memory_loading_file", new String[]
                             { file }),
@@ -632,7 +637,7 @@ public class FileLoader implements Runnable
     // memory
     // after
     // load
-    if (Desktop.desktop != null && Desktop.desktop.isShowMemoryUsage())
+    if (Desktop.getDesktopPane() != null && Desktop.getDesktopPane().isShowMemoryUsage())
     {
       if (alignFrame != null)
       {
@@ -654,9 +659,9 @@ public class FileLoader implements Runnable
       }
     }
     // remove the visual delay indicator
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
-      Desktop.instance.stopLoading();
+      Desktop.getInstance().stopLoading();
     }
 
   }
index 15cbe4e..b8a4d6a 100755 (executable)
@@ -335,6 +335,11 @@ public class IdentifyFile
             reply = FileFormat.Rnaml;
             break;
           }
+          if (upper.substring(lessThan).startsWith("<BSML"))
+          {
+            reply = FileFormat.BSML;
+            break;
+          }
         }
 
         if ((data.length() < 1) || (data.indexOf("#") == 0))
index a56f2af..48a6903 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.io;
 
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceI;
+import jalview.util.Platform;
 
 import java.util.List;
 
@@ -95,12 +96,20 @@ public class ModellerDescription
     }
   };
 
+  private static Regex VALIDATION_REGEX;
+
+  private static Regex getRegex()
+  {
+    return (VALIDATION_REGEX == null
+            ? VALIDATION_REGEX = Platform
+                    .newRegex("\\s*((([-0-9]+).?)|FIRST|LAST|@)", null)
+            : VALIDATION_REGEX);
+  }
+
   private resCode validResidueCode(String field)
   {
     Integer val = null;
-    Regex r = new Regex(
-            "\\s*((([-0-9]+).?)|FIRST|LAST|@)");
-
+    Regex r = getRegex();
     if (!r.search(field))
     {
       return null; // invalid
index f3eaa45..ea31e67 100755 (executable)
@@ -28,6 +28,7 @@ package jalview.io;
 
 import jalview.datamodel.SequenceNode;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -37,6 +38,8 @@ import java.util.StringTokenizer;
 
 import com.stevesoft.pat.Regex;
 
+// TODO This class does not conform to Java standards for field name capitalization.
+
 /**
  * Parse a new hanpshire style tree Caveats: NHX files are NOT supported and the
  * tree distances and topology are unreliable when they are parsed. TODO: on
@@ -76,7 +79,7 @@ import com.stevesoft.pat.Regex;
  */
 public class NewickFile extends FileParse
 {
-  SequenceNode root;
+  private SequenceNode root;
 
   private boolean HasBootstrap = false;
 
@@ -85,21 +88,90 @@ public class NewickFile extends FileParse
   private boolean RootHasDistance = false;
 
   // File IO Flags
-  boolean ReplaceUnderscores = false;
+  private boolean ReplaceUnderscores = false;
+
+  private boolean printRootInfo = true;
+
+  private static final int REGEX_PERL_NODE_REQUIRE_QUOTE = 0;
+
+  private static final int REGEX_PERL_NODE_ESCAPE_QUOTE = 1;
+
+  private static final int REGEX_PERL_NODE_UNQUOTED_WHITESPACE = 2;
+
+  private static final int REGEX_MAJOR_SYMS = 3;
+
+  private static final int REGEX_QNODE_NAME = 4;
+
+  private static final int REGEX_COMMENT = 5;
+
+  private static final int REGEX_UQNODE_NAME = 6;
 
-  boolean printRootInfo = true;
+  private static final int REGEX_NBOOTSTRAP = 7;
+
+  private static final int REGEX_NDIST = 8;
+
+  private static final int REGEX_NO_LINES = 9;
+
+  private static final int REGEX_PERL_EXPAND_QUOTES = 10;
+
+  private static final int REGEX_MAX = 11;
+
+  private static final Regex[] REGEX = new Regex[REGEX_MAX];
+
+  private static Regex getRegex(int id)
+  {
+    if (REGEX[id] == null)
+    {
+      String code = null;
+      String code2 = null;
+      String codePerl = null;
+      switch (id)
+      {
+      case REGEX_PERL_NODE_REQUIRE_QUOTE:
+        codePerl = "m/[\\[,:'()]/";
+        break;
+      case REGEX_PERL_NODE_ESCAPE_QUOTE:
+        codePerl = "s/'/''/";
+        break;
+      case REGEX_PERL_NODE_UNQUOTED_WHITESPACE:
+        codePerl = "s/\\/w/_/";
+        break;
+      case REGEX_PERL_EXPAND_QUOTES:
+        codePerl = "s/''/'/";
+        break;
+      case REGEX_MAJOR_SYMS:
+        code = "[(\\['),;]";
+        break;
+      case REGEX_QNODE_NAME:
+        code = "'([^']|'')+'";
+        break;
+      case REGEX_COMMENT:
+        code = "]";
+        break;
+      case REGEX_UQNODE_NAME:
+        code = "\\b([^' :;\\](),]+)";
+        break;
+      case REGEX_NBOOTSTRAP:
+        code = "\\s*([0-9+]+)\\s*:";
+        break;
+      case REGEX_NDIST:
+        code = ":([-0-9Ee.+]+)";
+        break;
+      case REGEX_NO_LINES:
+        code = "\n+";
+        code2 = "";
+        break;
+      default:
+        return null;
+      }
+      return codePerl == null ? Platform.newRegex(code, code2)
+              : Platform.newRegexPerl(codePerl);
+    }
+    return REGEX[id];
+  }
 
-  private Regex[] NodeSafeName = new Regex[] {
-      new Regex().perlCode("m/[\\[,:'()]/"), // test for
-      // requiring
-      // quotes
-      new Regex().perlCode("s/'/''/"), // escaping quote
-      // characters
-      new Regex().perlCode("s/\\/w/_/") // unqoted whitespace
-      // transformation
-  };
 
-  char QuoteChar = '\'';
+  private char quoteChar = '\'';
 
   /**
    * Creates a new NewickFile object.
@@ -257,6 +329,7 @@ public class NewickFile extends FileParse
    */
   public void parse() throws IOException
   {
+    Platform.ensureRegex();
     String nf;
 
     { // fill nf with complete tree file
@@ -294,8 +367,7 @@ public class NewickFile extends FileParse
     boolean ascending = false; // flag indicating that we are leaving the
     // current node
 
-    Regex majorsyms = new Regex(
-            "[(\\['),;]");
+    Regex majorsyms = getRegex(REGEX_MAJOR_SYMS); // "[(\\['),;]"
 
     int nextcp = 0;
     int ncp = cp;
@@ -354,8 +426,7 @@ public class NewickFile extends FileParse
       // Deal with quoted fields
       case '\'':
 
-        Regex qnodename = new Regex(
-                "'([^']|'')+'");
+        Regex qnodename = getRegex(REGEX_QNODE_NAME);// "'([^']|'')+'");
 
         if (qnodename.searchFrom(nf, fcp))
         {
@@ -363,8 +434,7 @@ public class NewickFile extends FileParse
           nodename = new String(
                   qnodename.stringMatched().substring(1, nl - 1));
           // unpack any escaped colons
-          Regex xpandquotes = Regex
-                  .perlCode("s/''/'/");
+          Regex xpandquotes = getRegex(REGEX_PERL_EXPAND_QUOTES);
           String widernodename = xpandquotes.replaceAll(nodename);
           nodename = widernodename;
           // jump to after end of quoted nodename
@@ -398,8 +468,7 @@ public class NewickFile extends FileParse
            * '"+nf.substring(cp,fcp)+"'"); }
            */
           // verify termination.
-          Regex comment = new Regex(
-                  "]");
+          Regex comment = getRegex(REGEX_COMMENT); // "]"
           if (comment.searchFrom(nf, fcp))
           {
             // Skip the comment field
@@ -430,12 +499,9 @@ public class NewickFile extends FileParse
                   + fstring.substring(cend + 1);
 
         }
-        Regex uqnodename = new Regex(
-                "\\b([^' :;\\](),]+)");
-        Regex nbootstrap = new Regex(
-                "\\s*([0-9+]+)\\s*:");
-        Regex ndist = new Regex(
-                ":([-0-9Ee.+]+)");
+        Regex uqnodename = getRegex(REGEX_UQNODE_NAME);// "\\b([^' :;\\](),]+)"
+        Regex nbootstrap = getRegex(REGEX_NBOOTSTRAP);// "\\s*([0-9+]+)\\s*:");
+        Regex ndist = getRegex(REGEX_NDIST);// ":([-0-9Ee.+]+)");
 
         if (!parsednodename && uqnodename.search(fstring)
                 && ((uqnodename.matchedFrom(1) == 0) || (fstring
@@ -790,7 +856,7 @@ public class NewickFile extends FileParse
    */
   char getQuoteChar()
   {
-    return QuoteChar;
+    return quoteChar;
   }
 
   /**
@@ -803,8 +869,8 @@ public class NewickFile extends FileParse
    */
   char setQuoteChar(char c)
   {
-    char old = QuoteChar;
-    QuoteChar = c;
+    char old = quoteChar;
+    quoteChar = c;
 
     return old;
   }
@@ -819,13 +885,15 @@ public class NewickFile extends FileParse
    */
   private String nodeName(String name)
   {
-    if (NodeSafeName[0].search(name))
+    if (getRegex(REGEX_PERL_NODE_REQUIRE_QUOTE).search(name))
     {
-      return QuoteChar + NodeSafeName[1].replaceAll(name) + QuoteChar;
+      return quoteChar
+              + getRegex(REGEX_PERL_NODE_ESCAPE_QUOTE).replaceAll(name)
+              + quoteChar;
     }
     else
     {
-      return NodeSafeName[2].replaceAll(name);
+      return getRegex(REGEX_PERL_NODE_UNQUOTED_WHITESPACE).replaceAll(name);
     }
   }
 
@@ -972,7 +1040,7 @@ public class NewickFile extends FileParse
       trf.parse();
       System.out.println("Original file :\n");
 
-      Regex nonl = new Regex("\n+", "");
+      Regex nonl = getRegex(REGEX_NO_LINES);// "\n+", "");
       System.out.println(nonl.replaceAll(newickfile.toString()) + "\n");
 
       System.out.println("Parsed file.\n");
index 4d3ddc1..9551992 100644 (file)
@@ -26,6 +26,7 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.io.BufferedReader;
 import java.io.FileNotFoundException;
@@ -227,7 +228,7 @@ public class RnamlFile extends AlignFile
       dataName = dataName.substring(0, b - 1);
     }
     b = 0;
-    Regex m = new Regex("[\\/]?([-A-Za-z0-9]+)\\.?");
+    Regex m = Platform.newRegex("[\\/]?([-A-Za-z0-9]+)\\.?");
     String mm = dataName;
     while (m.searchFrom(dataName, b))
     {
index 1a5072d..d5c3ed3 100644 (file)
@@ -238,7 +238,7 @@ public class SequenceAnnotationReport
       {
         if (sb0.length() > 6)
         {
-          sb.append("<br/>");
+          sb.append("<br>");
         }
         sb.append(feature.getType()).append(" ").append(begin).append(":")
                 .append(end);
@@ -248,7 +248,7 @@ public class SequenceAnnotationReport
 
     if (sb0.length() > 6)
     {
-      sb.append("<br/>");
+      sb.append("<br>");
     }
     // TODO: remove this hack to display link only features
     boolean linkOnly = feature.getValue("linkonly") != null;
@@ -277,7 +277,9 @@ public class SequenceAnnotationReport
         int linkindex = description.toLowerCase().indexOf("<a ");
         boolean hasLink = linkindex > -1
                 && linkindex < MAX_DESCRIPTION_LENGTH;
-        if (description.length() > MAX_DESCRIPTION_LENGTH && !hasLink)
+        if (
+                // BH suggestion maxlength == 0 && 
+                description.length() > MAX_DESCRIPTION_LENGTH && !hasLink)
         {
           description = description.substring(0, MAX_DESCRIPTION_LENGTH)
                   + ELLIPSIS;
@@ -396,7 +398,7 @@ public class SequenceAnnotationReport
           {
             for (List<String> urllink : createLinksFrom(null, urlstring))
             {
-              sb.append("<br/> <a href=\""
+              sb.append("<br> <a href=\""
                       + urllink.get(3)
                       + "\" target=\""
                       + urllink.get(0)
@@ -405,7 +407,7 @@ public class SequenceAnnotationReport
                               .equals(urllink.get(1).toLowerCase()) ? urllink
                               .get(0) : (urllink.get(0) + ":" + urllink
                                               .get(1)))
-                      + "</a><br/>");
+                      + "</a><br>");
             }
           } catch (Exception x)
           {
@@ -594,7 +596,7 @@ public class SequenceAnnotationReport
       countForSource++;
       if (countForSource == 1 || !summary)
       {
-        sb.append("<br/>");
+        sb.append("<br>");
       }
       if (countForSource <= MAX_REFS_PER_SOURCE || !summary)
       {
@@ -620,11 +622,11 @@ public class SequenceAnnotationReport
     }
     if (moreSources)
     {
-      sb.append("<br/>").append(source).append(COMMA).append(ELLIPSIS);
+      sb.append("<br>").append(source).append(COMMA).append(ELLIPSIS);
     }
     if (ellipsis)
     {
-      sb.append("<br/>(");
+      sb.append("<br>(");
       sb.append(MessageManager.getString("label.output_seq_details"));
       sb.append(")");
     }
index cbd9a38..50112c8 100644 (file)
  */
 package jalview.io;
 
+import jalview.analysis.Rna;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
+import jalview.util.DBRefUtils;
+import jalview.util.Format;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
@@ -39,21 +56,6 @@ import com.stevesoft.pat.Regex;
 import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
 import fr.orsay.lri.varna.factories.RNAFactory;
 import fr.orsay.lri.varna.models.rna.RNA;
-import jalview.analysis.Rna;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.Mapping;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.schemes.ResidueProperties;
-import jalview.util.Comparison;
-import jalview.util.DBRefUtils;
-import jalview.util.Format;
-import jalview.util.MessageManager;
 
 // import org.apache.log4j.*;
 
@@ -80,12 +82,103 @@ public class StockholmFile extends AlignFile
   private static final char UNDERSCORE = '_';
   
   // WUSS extended symbols. Avoid ambiguity with protein SS annotations by using NOT_RNASS first.
+
   public static final String RNASS_BRACKETS = "<>[](){}AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
 
+  public static final int REGEX_STOCKHOLM = 0;
+
+  public static final int REGEX_BRACKETS = 1;
+
   // use the following regex to decide an annotations (whole) line is NOT an RNA
   // SS (it contains only E,H,e,h and other non-brace/non-alpha chars)
-  private static final Regex NOT_RNASS = new Regex(
-          "^[^<>[\\](){}A-DF-Za-df-z]*$");
+  public static final int REGEX_NOT_RNASS = 2;
+
+  private static final int REGEX_ANNOTATION = 3;
+
+  private static final int REGEX_PFAM = 4;
+
+  private static final int REGEX_RFAM = 5;
+
+  private static final int REGEX_ALIGN_END = 6;
+
+  private static final int REGEX_SPLIT_ID = 7;
+
+  private static final int REGEX_SUBTYPE = 8;
+
+  private static final int REGEX_ANNOTATION_LINE = 9;
+
+  private static final int REGEX_REMOVE_ID = 10;
+
+  private static final int REGEX_OPEN_PAREN = 11;
+
+  private static final int REGEX_CLOSE_PAREN = 12;
+
+  public static final int REGEX_MAX = 13;
+
+  private static Regex REGEX[] = new Regex[REGEX_MAX];
+
+  /**
+   * Centralize all actual Regex instantialization in Platform.
+   * // JBPNote: Why is this 'centralisation' better ?
+   * @param id
+   * @return
+   */
+  private static Regex getRegex(int id)
+  {
+    if (REGEX[id] == null)
+    {
+      String pat = null, pat2 = null;
+      switch (id)
+      {
+      case REGEX_STOCKHOLM:
+        pat = "# STOCKHOLM ([\\d\\.]+)";
+        break;
+      case REGEX_BRACKETS:
+        // for reference; not used
+        pat = "(<|>|\\[|\\]|\\(|\\)|\\{|\\})";
+        break;
+      case REGEX_NOT_RNASS:
+        pat = "^[^<>[\\](){}A-DF-Za-df-z]*$";
+        break;
+      case REGEX_ANNOTATION:
+        pat = "(\\w+)\\s*(.*)";
+        break;
+      case REGEX_PFAM:
+        pat = "PF[0-9]{5}(.*)";
+        break;
+      case REGEX_RFAM:
+        pat = "RF[0-9]{5}(.*)";
+        break;
+      case REGEX_ALIGN_END:
+        pat = "^\\s*\\/\\/";
+        break;
+      case REGEX_SPLIT_ID:
+        pat = "(\\S+)\\/(\\d+)\\-(\\d+)";
+        break;
+      case REGEX_SUBTYPE:
+        pat = "(\\S+)\\s+(\\S*)\\s+(.*)";
+        break;
+      case REGEX_ANNOTATION_LINE:
+        pat = "#=(G[FSRC]?)\\s+(.*)";
+        break;
+      case REGEX_REMOVE_ID:
+        pat = "(\\S+)\\s+(\\S+)";
+        break;
+      case REGEX_OPEN_PAREN:
+        pat = "(<|\\[)";
+        pat2 = "(";
+        break;
+      case REGEX_CLOSE_PAREN:
+        pat = "(>|\\])";
+        pat2 = ")";
+        break;
+      default:
+        return null;
+      }
+      REGEX[id] = Platform.newRegex(pat, pat2);
+    }
+    return REGEX[id];
+  }
 
   StringBuffer out; // output buffer
 
@@ -210,7 +303,7 @@ public class StockholmFile extends AlignFile
     // First, we have to check that this file has STOCKHOLM format, i.e. the
     // first line must match
 
-    r = new Regex("# STOCKHOLM ([\\d\\.]+)");
+    r = getRegex(REGEX_STOCKHOLM);
     if (!r.search(nextLine()))
     {
       throw new IOException(MessageManager
@@ -223,20 +316,23 @@ public class StockholmFile extends AlignFile
       // logger.debug("Stockholm version: " + version);
     }
 
-    // We define some Regexes here that will be used regularly later
-    rend = new Regex("^\\s*\\/\\/"); // Find the end of an alignment
-    p = new Regex("(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in
+    // We define some Regexes here that will be used regularily later
+    rend = getRegex(REGEX_ALIGN_END);//"^\\s*\\/\\/"); // Find the end of an alignment
+    p = getRegex(REGEX_SPLIT_ID);//"(\\S+)\\/(\\d+)\\-(\\d+)"); // split sequence id in
     // id/from/to
-    s = new Regex("(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses annotation subtype
-    r = new Regex("#=(G[FSRC]?)\\s+(.*)"); // Finds any annotation line
-    x = new Regex("(\\S+)\\s+(\\S+)"); // split id from sequence
+    s = getRegex(REGEX_SUBTYPE);// "(\\S+)\\s+(\\S*)\\s+(.*)"); // Parses
+                                // annotation subtype
+    r = getRegex(REGEX_ANNOTATION_LINE);// "#=(G[FSRC]?)\\s+(.*)"); // Finds any
+                                        // annotation line
+    x = getRegex(REGEX_REMOVE_ID);// "(\\S+)\\s+(\\S+)"); // split id from
+                                  // sequence
 
     // Convert all bracket types to parentheses (necessary for passing to VARNA)
-    Regex openparen = new Regex("(<|\\[)", "(");
-    Regex closeparen = new Regex("(>|\\])", ")");
+    Regex openparen = getRegex(REGEX_OPEN_PAREN);//"(<|\\[)", "(");
+    Regex closeparen = getRegex(REGEX_CLOSE_PAREN);//"(>|\\])", ")");
 
 //    // Detect if file is RNA by looking for bracket types
-//    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
+    // Regex detectbrackets = getRegex("(<|>|\\[|\\]|\\(|\\))");
 
     rend.optimize();
     p.optimize();
@@ -258,8 +354,8 @@ public class StockholmFile extends AlignFile
         this.noSeqs = seqs.size();
 
         String dbsource = null;
-        Regex pf = new Regex("PF[0-9]{5}(.*)"); // Finds AC for Pfam
-        Regex rf = new Regex("RF[0-9]{5}(.*)"); // Finds AC for Rfam
+        Regex pf = getRegex(REGEX_PFAM); // Finds AC for Pfam
+        Regex rf = getRegex(REGEX_RFAM); // Finds AC for Rfam
         if (getAlignmentProperty("AC") != null)
         {
           String dbType = getAlignmentProperty("AC").toString();
@@ -328,14 +424,14 @@ public class StockholmFile extends AlignFile
 
           if (accAnnotations != null && accAnnotations.containsKey("AC"))
           {
-            String dbr = (String) accAnnotations.get("AC");
-            if (dbr != null)
-            {
-              // we could get very clever here - but for now - just try to
+              String dbr = (String) accAnnotations.get("AC");
+              if (dbr != null)
+              {
+                // we could get very clever here - but for now - just try to
               // guess accession type from type of sequence, source of alignment plus
               // structure
-              // of accession
-              guessDatabaseFor(seqO, dbr, dbsource);
+                // of accession
+                guessDatabaseFor(seqO, dbr, dbsource);
             }
             // else - do what ? add the data anyway and prompt the user to
             // specify what references these are ?
@@ -500,7 +596,7 @@ public class StockholmFile extends AlignFile
            */
           // Let's save the annotations, maybe we'll be able to do something
           // with them later...
-          Regex an = new Regex("(\\w+)\\s*(.*)");
+          Regex an = getRegex(REGEX_ANNOTATION);
           if (an.search(annContent))
           {
             if (an.stringMatched(1).equals("NH"))
@@ -840,7 +936,8 @@ public class StockholmFile extends AlignFile
     if (type.equalsIgnoreCase("secondary structure"))
     {
       ss = true;
-      isrnass = !NOT_RNASS.search(annots); // sorry about the double negative
+      isrnass = !getRegex(REGEX_NOT_RNASS).search(annots); // sorry about the double
+                                                     // negative
                                            // here (it's easier for dealing with
                                            // other non-alpha-non-brace chars)
     }
@@ -942,6 +1039,7 @@ public class StockholmFile extends AlignFile
     return ref.getSource().toString() + " ; "
             + ref.getAccessionId().toString();
   }
+
   @Override
   public String print(SequenceI[] s, boolean jvSuffix)
   {
@@ -981,17 +1079,12 @@ public class StockholmFile extends AlignFile
         }
         else
         {
-          for (int idb = 0; idb < seq.getDBRefs().size(); idb++)
+          for (int idb = 0; idb < ndb; idb++)
           {
-            DBRefEntry dbref = seq.getDBRefs().get(idb);
+            DBRefEntry dbref = seqrefs.get(idb);
             dataRef.put(tmp, dbref_to_ac_record(dbref));
             // if we put in a uniprot or EMBL record then we're done:
-            if (isAA && DBRefSource.UNIPROT
-                    .equals(DBRefUtils.getCanonicalName(dbref.getSource())))
-            {
-              break;
-            }
-            if (!isAA && DBRefSource.EMBL
+            if ((isAA ? DBRefSource.UNIPROT : DBRefSource.EMBL)
                     .equals(DBRefUtils.getCanonicalName(dbref.getSource())))
             {
               break;
index 387dbfa..15c481d 100644 (file)
@@ -35,8 +35,10 @@ import jalview.io.vamsas.DatastoreRegistry;
 import jalview.io.vamsas.Rangetype;
 import jalview.project.Jalview2XML;
 import jalview.util.MessageManager;
+import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.AlignmentViewport;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -720,9 +722,9 @@ public class VamsasAppDatastore
       // /SAVE THE TREES
       // /////////////////////////////////
       // FIND ANY ASSOCIATED TREES
-      if (Desktop.desktop != null)
+      if (Desktop.getDesktopPane() != null)
       {
-        javax.swing.JInternalFrame[] frames = Desktop.instance
+        javax.swing.JInternalFrame[] frames = Desktop.getInstance()
                 .getAllFrames();
 
         for (int t = 0; t < frames.length; t++)
@@ -1320,7 +1322,7 @@ public class VamsasAppDatastore
             // and
             // mapValuesToString
             fromxml.setSkipList(skipList);
-            jalview.util.jarInputStreamProvider jprovider = new jalview.util.jarInputStreamProvider()
+            jarInputStreamProvider jprovider = new jarInputStreamProvider()
             {
 
               @Override
@@ -1338,6 +1340,12 @@ public class VamsasAppDatastore
                         "Returning client input stream for Jalview from Vamsas Document.");
                 return new JarInputStream(cappdata.getClientInputStream());
               }
+
+              @Override
+              public File getFile()
+              {
+                return null;
+              }
             };
             if (dojvsync)
             {
@@ -1368,7 +1376,7 @@ public class VamsasAppDatastore
           fromxml.setSkipList(skipList);
           fromxml.setObjectMappingTables(mapKeysToString(vobj2jv),
                   mapValuesToString(jv2vobj));
-          jalview.util.jarInputStreamProvider jarstream = new jalview.util.jarInputStreamProvider()
+          jarInputStreamProvider jarstream = new jarInputStreamProvider()
           {
 
             @Override
@@ -1386,6 +1394,12 @@ public class VamsasAppDatastore
                       "Returning user input stream for Jalview from Vamsas Document.");
               return new JarInputStream(cappdata.getUserInputStream());
             }
+
+            @Override
+            public File getFile()
+            {
+              return null;
+            }
           };
           if (dojvsync)
           {
@@ -1479,7 +1493,7 @@ public class VamsasAppDatastore
           if (mappings != null)
           {
             jalview.structure.StructureSelectionManager
-                    .getStructureSelectionManager(Desktop.instance)
+                    .getStructureSelectionManager(Desktop.getInstance())
                     .registerMappings(mappings);
           }
         }
index 63b78b2..e24c2f3 100755 (executable)
@@ -150,7 +150,7 @@ public class WSWUBlastClient
   {
     // This must be outside the run() body as java 1.5
     // will not return any value from the OptionPane to the expired thread.
-    int reply = JvOptionPane.showConfirmDialog(Desktop.desktop,
+    int reply = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
             "Automatically update suggested ids?",
             "Auto replace sequence ids", JvOptionPane.YES_NO_OPTION);
 
index eaf6ecd..fdcad08 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.io.cache;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 
 import java.util.Hashtable;
@@ -31,22 +33,27 @@ import java.util.LinkedHashSet;
  * @author tcnofoegbu
  *
  */
-public class AppCache
+public class AppCache implements ApplicationSingletonI
 {
+
+  public static AppCache getInstance()
+  {
+    return (AppCache) ApplicationSingletonProvider.getInstance(AppCache.class);
+  }
+
+  private AppCache()
+  {
+    cacheItems = new Hashtable<String, LinkedHashSet<String>>();
+  }
+
   public static final String DEFAULT_LIMIT = "99";
 
   public static final String CACHE_DELIMITER = ";";
 
-  private static AppCache instance = null;
-
   private static final String DEFAULT_LIMIT_KEY = ".DEFAULT_LIMIT";
 
   private Hashtable<String, LinkedHashSet<String>> cacheItems;
 
-  private AppCache()
-  {
-    cacheItems = new Hashtable<String, LinkedHashSet<String>>();
-  }
 
   /**
    * Method to obtain all the cache items for a given cache key
@@ -66,20 +73,6 @@ public class AppCache
   }
 
   /**
-   * Returns a singleton instance of AppCache
-   * 
-   * @return
-   */
-  public static AppCache getInstance()
-  {
-    if (instance == null)
-    {
-      instance = new AppCache();
-    }
-    return instance;
-  }
-
-  /**
    * Method for persisting cache items for a given cache key
    * 
    * @param cacheKey
index 1ef8848..e422ed4 100644 (file)
@@ -91,7 +91,7 @@ public class Gff3Helper extends GffHelperBase
       String atts = gff[ATTRIBUTES_COL];
       Map<String, List<String>> attributes = parseNameValuePairs(atts);
 
-      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+      SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
       if (so.isA(soTerm, SequenceOntologyI.PROTEIN_MATCH))
       {
         sf = processProteinMatch(attributes, seq, gff, align, newseqs,
@@ -385,7 +385,7 @@ public class Gff3Helper extends GffHelperBase
       desc = target.split(" ")[0];
     }
 
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     String type = sf.getType();
     if (so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT))
     {
index 948cdd2..141b677 100644 (file)
@@ -108,7 +108,7 @@ public class InterProScanHelper extends Gff3Helper
    */
   public static boolean recognises(String[] columns)
   {
-    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    SequenceOntologyI so = SequenceOntologyFactory.getSequenceOntology();
     String type = columns[TYPE_COL];
     if (so.isA(type, SequenceOntologyI.PROTEIN_MATCH)
             || (".".equals(columns[SOURCE_COL])
index 90cae7a..08a2c6a 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.io.gff;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * A factory class that returns a model of the Sequence Ontology. By default a
  * hard-coded subset is used (for the applet, or testing), or setInstance() can
@@ -28,21 +31,53 @@ package jalview.io.gff;
  * @author gmcarstairs
  *
  */
-public class SequenceOntologyFactory
+public class SequenceOntologyFactory implements ApplicationSingletonI
 {
-  private static SequenceOntologyI instance;
+  /**
+   * Answers an instance of this class for the current application context. Note
+   * that this supports running two JS 'applets' on the same page, one with the
+   * full Sequence Ontology (USE_FULL_SO = true) and one with a hard-coded
+   * subset (USE_FULL_SO = false). If this is overkill, could change this method
+   * to just return a common static instance.
+   * 
+   * @return
+   */
+  private static synchronized SequenceOntologyFactory getInstance()
+  {
+    return (SequenceOntologyFactory) ApplicationSingletonProvider
+            .getInstance(SequenceOntologyFactory.class);
+  }
+
+  private SequenceOntologyFactory()
+  {
+    // private singleton
+  }
+
 
-  public static synchronized SequenceOntologyI getInstance()
+  /**
+   * Answers the configured model of the Sequence Ontology.
+   * 
+   * @return
+   */
+  public static synchronized SequenceOntologyI getSequenceOntology()
   {
-    if (instance == null)
-    {
-      instance = new SequenceOntologyLite();
-    }
-    return instance;
+    SequenceOntologyFactory f = getInstance();
+    return (f.sequenceOntology == null
+            ? f.sequenceOntology = new SequenceOntologyLite()
+            : f.sequenceOntology);
   }
 
-  public static void setInstance(SequenceOntologyI so)
+  /**
+   * For testng only
+   * 
+   * @param so
+   */
+  public static void setSequenceOntology(SequenceOntologyI so)
   {
-    instance = so;
+    getInstance().sequenceOntology = so;
   }
+
+
+  private SequenceOntologyI sequenceOntology;
+
 }
index df78ab1..2b95081 100644 (file)
  */
 package jalview.io.packed;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
 import jalview.datamodel.AlignmentI;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.FileFormatI;
@@ -28,12 +34,6 @@ import jalview.io.FormatAdapter;
 import jalview.io.IdentifyFile;
 import jalview.io.packed.DataProvider.JvDataType;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
 public class ParsePackedSet
 {
 
index 0a582e5..402ffdf 100644 (file)
@@ -365,7 +365,7 @@ public class Sequencemapping extends Rangetype
     }
     bindjvvobj(mapping, sequenceMapping);
     jalview.structure.StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance)
+            .getStructureSelectionManager(Desktop.getInstance())
             .registerMapping(acf);
     // Try to link up any conjugate database references in the two sequences
     // matchConjugateDBRefs(from, to, mapping);
index bb7eb34..183328d 100644 (file)
@@ -90,10 +90,6 @@ public class Logger
   {
     switch (level.level)
     {
-    case Priority.FATAL_INT:
-    case Priority.ERROR_INT:
-    case Priority.WARN_INT:
-    case Priority.INFO_INT:
     case Priority.DEBUG_INT:
       log(o, e);
       break;
@@ -109,10 +105,8 @@ public class Logger
   {
     switch (level.level)
     {
-    case Priority.FATAL_INT:
-    case Priority.ERROR_INT:
-    case Priority.WARN_INT:
     case Priority.INFO_INT:
+    case Priority.DEBUG_INT:
       log(o, e);
       break;
     }
@@ -128,9 +122,9 @@ public class Logger
   {
     switch (level.level)
     {
-    case Priority.FATAL_INT:
-    case Priority.ERROR_INT:
     case Priority.WARN_INT:
+    case Priority.INFO_INT:
+    case Priority.DEBUG_INT:
       log(o, e);
       break;
     }
@@ -146,8 +140,10 @@ public class Logger
   {
     switch (level.level)
     {
-    case Priority.FATAL_INT:
     case Priority.ERROR_INT:
+    case Priority.WARN_INT:
+    case Priority.INFO_INT:
+    case Priority.DEBUG_INT:
       log(o, e);
       break;
     }
index 0d6a03f..25db31a 100755 (executable)
  */
 package jalview.jbgui;
 
-import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
-import jalview.analysis.GeneticCodeI;
-import jalview.analysis.GeneticCodes;
-import jalview.api.SplitContainerI;
-import jalview.bin.Cache;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.Preferences;
-import jalview.hmmer.HmmerCommand;
-import jalview.io.FileFormatException;
-import jalview.io.FileFormats;
-import jalview.schemes.ResidueColourScheme;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.GridLayout;
@@ -41,6 +27,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusAdapter;
 import java.awt.event.FocusEvent;
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
@@ -64,6 +51,21 @@ import javax.swing.event.ChangeEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.analysis.GeneticCodeI;
+import jalview.analysis.GeneticCodes;
+import jalview.api.SplitContainerI;
+import jalview.bin.Cache;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.Preferences;
+import jalview.hmmer.HmmerCommand;
+import jalview.io.FileFormatException;
+import jalview.io.FileFormats;
+import jalview.schemes.ResidueColourScheme;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
+
 @SuppressWarnings("serial")
 public class GAlignFrame extends JInternalFrame
 {
@@ -231,7 +233,8 @@ public class GAlignFrame extends JInternalFrame
     {
 
       // for Web-page embedding using id=align-frame-div
-      setName("jalview-alignment");
+      setName(Platform.getAppID("alignment"));
+
 
       jbInit();
       setJMenuBar(alignFrameMenuBar);
@@ -286,15 +289,12 @@ public class GAlignFrame extends JInternalFrame
     };
   
     // FIXME getDefaultToolkit throws an exception in Headless mode
-    KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+    KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, Platform.SHORTCUT_KEY_MASK | InputEvent.SHIFT_DOWN_MASK,
             false);
     addMenuActionAndAccelerator(keyStroke, saveAs, al);
   
     closeMenuItem.setText(MessageManager.getString("action.close"));
-    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+    keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W, Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -320,7 +320,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem selectAllSequenceMenuItem = new JMenuItem(
             MessageManager.getString("action.select_all"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -347,7 +347,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem invertSequenceMenuItem = new JMenuItem(
             MessageManager.getString("action.invert_sequence_selection"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -383,7 +383,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem remove2LeftMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_left"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_L,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -397,7 +397,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem remove2RightMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_right"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -411,7 +411,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem removeGappedColumnMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_empty_columns"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -425,8 +425,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem removeAllGapsMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_all_gaps"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -553,7 +553,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem removeRedundancyMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_redundancy"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -755,7 +755,7 @@ public class GAlignFrame extends JInternalFrame
     undoMenuItem.setEnabled(false);
     undoMenuItem.setText(MessageManager.getString("action.undo"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -769,7 +769,7 @@ public class GAlignFrame extends JInternalFrame
     redoMenuItem.setEnabled(false);
     redoMenuItem.setText(MessageManager.getString("action.redo"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -793,7 +793,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem printMenuItem = new JMenuItem(
             MessageManager.getString("action.print"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -819,7 +819,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem findMenuItem = new JMenuItem(
             MessageManager.getString("action.find"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     findMenuItem.setToolTipText(JvSwingUtils.wrapTooltip(true,
             MessageManager.getString("label.find_tip")));
     al = new ActionListener()
@@ -981,7 +981,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem deleteGroups = new JMenuItem(
             MessageManager.getString("action.undefine_groups"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1006,7 +1006,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem createGroup = new JMenuItem(
             MessageManager.getString("action.create_group"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1020,8 +1020,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem unGroup = new JMenuItem(
             MessageManager.getString("action.remove_group"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -1035,7 +1035,7 @@ public class GAlignFrame extends JInternalFrame
   
     copy.setText(MessageManager.getString("action.copy"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
 
     al = new ActionListener()
     {
@@ -1049,7 +1049,7 @@ public class GAlignFrame extends JInternalFrame
   
     cut.setText(MessageManager.getString("action.cut"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1075,8 +1075,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem pasteNew = new JMenuItem(
             MessageManager.getString("label.to_new_alignment"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -1098,7 +1098,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem pasteThis = new JMenuItem(
             MessageManager.getString("label.to_this_alignment"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1247,6 +1247,24 @@ public class GAlignFrame extends JInternalFrame
       @Override
       public void menuSelected(MenuEvent e)
       {
+        enableSortMenuOptions();
+      }
+
+      @Override
+      public void menuDeselected(MenuEvent e)
+      {
+      }
+
+      @Override
+      public void menuCanceled(MenuEvent e)
+      {
+      }
+    });
+    sortByTreeMenu.addMenuListener(new MenuListener()
+    {
+      @Override
+      public void menuSelected(MenuEvent e)
+      {
         buildTreeSortMenu();
       }
   
@@ -1628,8 +1646,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem invertColSel = new JMenuItem(
             MessageManager.getString("action.invert_column_selection"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
-                    | jalview.util.ShortcutKeyMaskExWrapper.ALT_DOWN_MASK,
+            Platform.SHORTCUT_KEY_MASK
+                    | InputEvent.ALT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -1692,7 +1710,7 @@ public class GAlignFrame extends JInternalFrame
   
     JMenuItem save = new JMenuItem(MessageManager.getString("action.save"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1717,7 +1735,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem newView = new JMenuItem(
             MessageManager.getString("action.new_view"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
+            Platform.SHORTCUT_KEY_MASK, false);
     al = new ActionListener()
     {
       @Override
@@ -1794,8 +1812,8 @@ public class GAlignFrame extends JInternalFrame
     });
     JMenuItem selectHighlighted = new JMenuItem(
             MessageManager.getString("action.select_highlighted_columns"));
-    selectHighlighted.setToolTipText(
-            MessageManager.getString("tooltip.select_highlighted_columns"));
+    selectHighlighted.setToolTipText(JvSwingUtils.wrapTooltip(true, 
+            MessageManager.getString("tooltip.select_highlighted_columns")));
     al = new ActionListener()
     {
       @Override
@@ -2181,6 +2199,10 @@ public class GAlignFrame extends JInternalFrame
 
   }
 
+  protected void enableSortMenuOptions()
+  {
+  }
+  
   protected void loadVcf_actionPerformed()
   {
   }
@@ -3019,4 +3041,6 @@ public class GAlignFrame extends JInternalFrame
   protected void showComplement_actionPerformed(boolean complement)
   {
   }
+  
+
 }
index 1c9e907..121917e 100644 (file)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.gui.JvSwingUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Font;
@@ -135,14 +136,12 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     });
     close.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_W,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
+            Platform.SHORTCUT_KEY_MASK,
             false));
     selectAll.setText(MessageManager.getString("action.select_all"));
     selectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_A,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
+            Platform.SHORTCUT_KEY_MASK,
             false));
     selectAll.addActionListener(new ActionListener()
     {
@@ -156,8 +155,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     save.setText(MessageManager.getString("action.save"));
     save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_S,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
+            Platform.SHORTCUT_KEY_MASK,
             false));
     save.addActionListener(new ActionListener()
     {
@@ -169,8 +167,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     });
     copyItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_C,
-            jalview.util.ShortcutKeyMaskExWrapper
-                    .getMenuShortcutKeyMaskEx(),
+            Platform.SHORTCUT_KEY_MASK,
             false));
 
     editMenubar.add(jMenu1);
index 3dc4111..83394cf 100755 (executable)
@@ -20,9 +20,6 @@
  */
 package jalview.jbgui;
 
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
@@ -38,6 +35,10 @@ import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
 
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
 /**
  * DOCUMENT ME!
  * 
@@ -122,7 +123,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
     selectAll.setText(MessageManager.getString("action.select_all"));
     selectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_A,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
+            Platform.SHORTCUT_KEY_MASK, false));
     selectAll.addActionListener(new ActionListener()
     {
       @Override
@@ -135,7 +136,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
     save.setText(MessageManager.getString("action.save"));
     save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_S,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
+            Platform.SHORTCUT_KEY_MASK, false));
     save.addActionListener(new ActionListener()
     {
       @Override
@@ -146,10 +147,10 @@ public class GCutAndPasteTransfer extends JInternalFrame
     });
     copyItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_C,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
+            Platform.SHORTCUT_KEY_MASK, false));
     pasteMenu.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_V,
-            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
+            Platform.SHORTCUT_KEY_MASK, false));
     editMenubar.add(jMenu1);
     editMenubar.add(editMenu);
     textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 12));
index e4a5905..0c179e8 100755 (executable)
@@ -45,7 +45,7 @@ import javax.swing.JMenuItem;
 public class GDesktop extends JFrame
 {
 
-  protected static JMenu windowMenu = new JMenu();
+  protected JMenu windowMenu = new JMenu();
 
   JMenuBar desktopMenubar = new JMenuBar();
 
@@ -139,14 +139,15 @@ public class GDesktop extends JFrame
    */
   private void jbInit() throws Exception
   {
-    setName("jalview-desktop");
+
+    setName(Platform.getAppID("desktop"));
     FileMenu.setText(MessageManager.getString("action.file"));
     HelpMenu.setText(MessageManager.getString("action.help"));
     inputLocalFileMenuItem
             .setText(MessageManager.getString("label.load_tree_from_file"));
     inputLocalFileMenuItem.setAccelerator(
             javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O,
-                    jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(),
+                    Platform.SHORTCUT_KEY_MASK,
                     false));
     inputLocalFileMenuItem
             .addActionListener(new java.awt.event.ActionListener()
index a6498d2..aa2f549 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -88,7 +89,7 @@ public class GPCAPanel extends JInternalFrame
 
   private void jbInit() throws Exception
   {
-    setName("jalview-pca");
+    setName(Platform.getAppID("pca"));
     this.getContentPane().setLayout(new BorderLayout());
     JPanel jPanel2 = new JPanel();
     jPanel2.setLayout(new FlowLayout());
index b354acb..26c37d0 100755 (executable)
@@ -1444,20 +1444,23 @@ public class GPreferences extends JPanel
     viewerLabel.setBounds(new Rectangle(10, ypos, 200, height));
     structureTab.add(viewerLabel);
 
-    structViewer.setFont(LABEL_FONT);
-    structViewer.setBounds(new Rectangle(160, ypos, 120, height));
-    structViewer.addItem(ViewerType.JMOL.name());
-    structViewer.addItem(ViewerType.CHIMERA.name());
-    structViewer.addActionListener(new ActionListener()
+    if (!Platform.isJS())
     {
-      @Override
-      public void actionPerformed(ActionEvent e)
+      structViewer.setFont(LABEL_FONT);
+      structViewer.setBounds(new Rectangle(160, ypos, 120, height));
+      structViewer.addItem(ViewerType.JMOL.name());
+      structViewer.addItem(ViewerType.CHIMERA.name());
+      structViewer.addActionListener(new ActionListener()
       {
-        structureViewer_actionPerformed(
-                (String) structViewer.getSelectedItem());
-      }
-    });
-    structureTab.add(structViewer);
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          structureViewer_actionPerformed(
+                  (String) structViewer.getSelectedItem());
+        }
+      });
+      structureTab.add(structViewer);
+    }
 
     ypos += lineSpacing;
     JLabel pathLabel = new JLabel();
@@ -1520,14 +1523,14 @@ public class GPreferences extends JPanel
     /*
      * hide Chimera options in JalviewJS
      */
-    if (Platform.isJS()) 
+    if (Platform.isJS())
     {
       pathLabel.setVisible(false);
       chimeraPath.setVisible(false);
       viewerLabel.setVisible(false);
       structViewer.setVisible(false);
     }
-    
+
     return structureTab;
   }
 
@@ -2405,7 +2408,7 @@ public class GPreferences extends JPanel
     boolean ret = false;
     String warningMessage = MessageManager
             .getString("label.warning_confirm_change_reverse");
-    int confirm = JvOptionPane.showConfirmDialog(Desktop.desktop,
+    int confirm = JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
             warningMessage,
             MessageManager.getString("label.change_increment_decrement"),
             JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE);
index a43e504..2ec7051 100755 (executable)
@@ -219,7 +219,7 @@ public class GSequenceLink extends JPanel
       return true;
     }
 
-    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.desktop,
+    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.getDesktopPane(),
             MessageManager.getString("warn.url_must_contain"),
             MessageManager.getString("label.invalid_url"),
             JvOptionPane.WARNING_MESSAGE);
@@ -228,7 +228,7 @@ public class GSequenceLink extends JPanel
 
   public void notifyDuplicate()
   {
-    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.desktop,
+    JvOptionPane.showInternalMessageDialog(jalview.gui.Desktop.getDesktopPane(),
             MessageManager.getString("warn.name_cannot_be_duplicate"),
             MessageManager.getString("label.invalid_name"),
             JvOptionPane.WARNING_MESSAGE);
index dfee3e2..1e7cf37 100644 (file)
@@ -24,6 +24,7 @@ import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.gui.ColourMenuHelper.ColourChangeListener;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.GridLayout;
@@ -89,7 +90,7 @@ public abstract class GStructureViewer extends JInternalFrame
   private void jbInit() throws Exception
   {
 
-    setName("jalview-structureviewer");
+    setName(Platform.getAppID("structureviewer"));
 
     JMenuBar menuBar = new JMenuBar();
     this.setJMenuBar(menuBar);
index d184e76..3aff0e0 100755 (executable)
@@ -22,6 +22,7 @@ package jalview.jbgui;
 
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -92,7 +93,7 @@ public class GTreePanel extends JInternalFrame
 
   private void jbInit() throws Exception
   {
-    setName("jalview-tree");
+    setName(Platform.getAppID("tree"));
     this.getContentPane().setLayout(borderLayout1);
     this.setBackground(Color.white);
     this.setFont(new java.awt.Font("Verdana", 0, 12));
index 27a068c..4b10546 100755 (executable)
@@ -20,9 +20,6 @@
  */
 package jalview.jbgui;
 
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
-
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -44,6 +41,9 @@ import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 import javax.swing.colorchooser.AbstractColorChooserPanel;
 
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+
 /**
  * DOCUMENT ME!
  * 
index 70ef538..e88a4a1 100644 (file)
@@ -28,6 +28,7 @@ import jalview.analysis.Conservation;
 import jalview.analysis.PCA;
 import jalview.analysis.scoremodels.ScoreModels;
 import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
@@ -61,6 +62,7 @@ import jalview.gui.AlignmentPanel;
 import jalview.gui.AppVarna;
 import jalview.gui.ChimeraViewFrame;
 import jalview.gui.Desktop;
+import jalview.gui.FeatureRenderer;
 import jalview.gui.JvOptionPane;
 import jalview.gui.OOMWarning;
 import jalview.gui.PCAPanel;
@@ -84,7 +86,6 @@ import jalview.schemes.ColourSchemeProperty;
 import jalview.schemes.FeatureColour;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
-import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.Format;
 import jalview.util.MessageManager;
@@ -153,6 +154,7 @@ import jalview.xml.binding.jalview.ThresholdType;
 import jalview.xml.binding.jalview.VAMSAS;
 
 import java.awt.Color;
+import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Rectangle;
 import java.io.BufferedReader;
@@ -163,10 +165,10 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
 import java.math.BigInteger;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -271,6 +273,25 @@ public class Jalview2XML
   private Map<RnaModel, String> rnaSessions = new HashMap<>();
 
   /**
+   * contains last error message (if any) encountered by XML loader.
+   */
+  String errorMessage = null;
+
+  /**
+   * flag to control whether the Jalview2XML_V1 parser should be deferred to if
+   * exceptions are raised during project XML parsing
+   */
+  public boolean attemptversion1parse = false;
+
+  /*
+   * JalviewJS only -- to allow read file bytes to be saved in the
+   * created AlignFrame, allowing File | Reload of a project file to work
+   * 
+   * BH 2019 JAL-3436
+   */
+  private File jarFile;
+
+  /**
    * A helper method for safely using the value of an optional attribute that
    * may be null if not present in the XML. Answers the boolean value, or false
    * if null.
@@ -430,7 +451,7 @@ public class Jalview2XML
    * @param _jmap
    * @return
    */
-  public SeqFref newMappingRef(final String sref,
+  protected SeqFref newMappingRef(final String sref,
           final jalview.datamodel.Mapping _jmap)
   {
     SeqFref fref = new SeqFref(sref, "Mapping")
@@ -452,7 +473,7 @@ public class Jalview2XML
     return fref;
   }
 
-  public SeqFref newAlcodMapRef(final String sref,
+  protected SeqFref newAlcodMapRef(final String sref,
           final AlignedCodonFrame _cf,
           final jalview.datamodel.Mapping _jmap)
   {
@@ -484,7 +505,7 @@ public class Jalview2XML
     return fref;
   }
 
-  public void resolveFrefedSequences()
+  protected void resolveFrefedSequences()
   {
     Iterator<SeqFref> nextFref = frefedSequence.iterator();
     int toresolve = frefedSequence.size();
@@ -629,13 +650,14 @@ public class Jalview2XML
    * core method for storing state for a set of AlignFrames.
    * 
    * @param frames
-   *          - frames involving all data to be exported (including containing
-   *          splitframes)
+   *          - frames involving all data to be exported (including those
+   *          contained in splitframes, though not the split frames themselves)
    * @param jout
    *          - project output stream
    */
   private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
   {
+
     Hashtable<String, AlignFrame> dsses = new Hashtable<>();
 
     /*
@@ -658,21 +680,22 @@ public class Jalview2XML
       for (int i = frames.size() - 1; i > -1; i--)
       {
         AlignFrame af = frames.get(i);
+        AlignViewport vp = af.getViewport();
         // skip ?
         if (skipList != null && skipList
-                .containsKey(af.getViewport().getSequenceSetId()))
+                .containsKey(vp.getSequenceSetId()))
         {
           continue;
         }
 
         String shortName = makeFilename(af, shortNames);
 
-        int apSize = af.getAlignPanels().size();
-
+        AlignmentI alignment = vp.getAlignment();
+        List<? extends AlignmentViewPanel> panels = af.getAlignPanels();
+        int apSize = panels.size();
         for (int ap = 0; ap < apSize; ap++)
-        {
-          AlignmentPanel apanel = (AlignmentPanel) af.getAlignPanels()
-                  .get(ap);
+          {
+          AlignmentPanel apanel = (AlignmentPanel) panels.get(ap);
           String fileName = apSize == 1 ? shortName : ap + shortName;
           if (!fileName.endsWith(".xml"))
           {
@@ -680,11 +703,17 @@ public class Jalview2XML
           }
 
           saveState(apanel, fileName, jout, viewIds);
-
-          String dssid = getDatasetIdRef(
-                  af.getViewport().getAlignment().getDataset());
+        }
+        if (apSize > 0)
+        {
+          // BH moved next bit out of inner loop, not that it really matters.
+          // so we are testing to make sure we actually have an alignment,
+          // apparently.
+          String dssid = getDatasetIdRef(alignment.getDataset());
           if (!dsses.containsKey(dssid))
           {
+            // We have not already covered this data by reference from another
+            // frame.
             dsses.put(dssid, af);
           }
         }
@@ -802,10 +831,22 @@ public class Jalview2XML
     }
   }
 
+  /**
+   * Each AlignFrame has a single data set associated with it. Note that none of
+   * these frames are split frames, because Desktop.getAlignFrames() collects
+   * top and bottom separately here.
+   * 
+   * @param dsses
+   * @param fileName
+   * @param jout
+   */
   private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
           String fileName, JarOutputStream jout)
   {
 
+    // Note that in saveAllFrames we have associated each specific dataset to
+    // ONE of its associated frames.
+
     for (String dssids : dsses.keySet())
     {
       AlignFrame _af = dsses.get(dssids);
@@ -832,7 +873,7 @@ public class Jalview2XML
    * @param out
    *          jar entry name
    */
-  public JalviewModel saveState(AlignmentPanel ap, String fileName,
+  protected JalviewModel saveState(AlignmentPanel ap, String fileName,
           JarOutputStream jout, List<String> viewIds)
   {
     return saveState(ap, fileName, false, jout, viewIds);
@@ -854,7 +895,7 @@ public class Jalview2XML
    * @param out
    *          jar entry name
    */
-  public JalviewModel saveState(AlignmentPanel ap, String fileName,
+  protected JalviewModel saveState(AlignmentPanel ap, String fileName,
           boolean storeDS, JarOutputStream jout, List<String> viewIds)
   {
     if (viewIds == null)
@@ -1089,7 +1130,7 @@ public class Jalview2XML
            * only view *should* be coped with sensibly.
            */
           // This must have been loaded, is it still visible?
-          JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+          JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
           String matchedFile = null;
           for (int f = frames.length - 1; f > -1; f--)
           {
@@ -1246,9 +1287,9 @@ public class Jalview2XML
     {
       // FIND ANY ASSOCIATED TREES
       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
-      if (Desktop.desktop != null)
+      if (Desktop.getDesktopPane() != null)
       {
-        JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+        JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
 
         for (int t = 0; t < frames.length; t++)
         {
@@ -1292,9 +1333,9 @@ public class Jalview2XML
     /*
      * save PCA viewers
      */
-    if (!storeDS && Desktop.desktop != null)
+    if (!storeDS && Desktop.getDesktopPane() != null)
     {
-      for (JInternalFrame frame : Desktop.desktop.getAllFrames())
+      for (JInternalFrame frame : Desktop.getDesktopPane().getAllFrames())
       {
         if (frame instanceof PCAPanel)
         {
@@ -1967,11 +2008,11 @@ public class Jalview2XML
           final SequenceI jds, List<String> viewIds, AlignmentPanel ap,
           boolean storeDataset)
   {
-    if (Desktop.desktop == null)
+    if (Desktop.getDesktopPane() == null)
     {
       return;
     }
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
     for (int f = frames.length - 1; f > -1; f--)
     {
       if (frames[f] instanceof AppVarna)
@@ -2766,17 +2807,6 @@ public class Jalview2XML
   }
 
   /**
-   * contains last error message (if any) encountered by XML loader.
-   */
-  String errorMessage = null;
-
-  /**
-   * flag to control whether the Jalview2XML_V1 parser should be deferred to if
-   * exceptions are raised during project XML parsing
-   */
-  public boolean attemptversion1parse = false;
-
-  /**
    * Load a jalview project archive from a jar file
    * 
    * @param file
@@ -2810,7 +2840,10 @@ public class Jalview2XML
     {
       try
       {
-        SwingUtilities.invokeAndWait(new Runnable()
+// was invokeAndWait
+         
+         // BH 2019 -- can't wait
+        SwingUtilities.invokeLater(new Runnable()
         {
           @Override
           public void run()
@@ -2823,55 +2856,61 @@ public class Jalview2XML
         System.err.println("Error loading alignment: " + x.getMessage());
       }
     }
+    this.jarFile = null;
     return af;
   }
 
        @SuppressWarnings("unused")
-       private jarInputStreamProvider createjarInputStreamProvider(final Object ofile) throws MalformedURLException {
-
-               // BH 2018 allow for bytes already attached to File object
-               try {
-                       String file = (ofile instanceof File ? ((File) ofile).getCanonicalPath() : ofile.toString());
+  private jarInputStreamProvider createjarInputStreamProvider(
+          final Object ofile) throws MalformedURLException
+  {
+    try
+    {
+      String file = (ofile instanceof File
+              ? ((File) ofile).getCanonicalPath()
+              : ofile.toString());
       byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
               : null;
-                       URL url = null;
-                       errorMessage = null;
-                       uniqueSetSuffix = null;
-                       seqRefIds = null;
-                       viewportsAdded.clear();
-                       frefedSequence = null;
-
-                       if (file.startsWith("http://")) {
-                               url = new URL(file);
-                       }
-                       final URL _url = url;
-                       return new jarInputStreamProvider() {
-
-                               @Override
-                               public JarInputStream getJarInputStream() throws IOException {
-                                       if (bytes != null) {
-//                                             System.out.println("Jalview2XML: opening byte jarInputStream for bytes.length=" + bytes.length);
-                                               return new JarInputStream(new ByteArrayInputStream(bytes));
-                                       }
-                                       if (_url != null) {
-//                                             System.out.println("Jalview2XML: opening url jarInputStream for " + _url);
-                                               return new JarInputStream(_url.openStream());
-                                       } else {
-//                                             System.out.println("Jalview2XML: opening file jarInputStream for " + file);
-                                               return new JarInputStream(new FileInputStream(file));
-                                       }
-                               }
-
-                               @Override
-                               public String getFilename() {
-                                       return file;
-                               }
-                       };
-               } catch (IOException e) {
-                       e.printStackTrace();
-                       return null;
-               }
-       }
+      if (bytes != null)
+      {
+        this.jarFile = (File) ofile;
+      }
+      errorMessage = null;
+      uniqueSetSuffix = null;
+      seqRefIds = null;
+      viewportsAdded.clear();
+      frefedSequence = null;
+
+      URL url = file.startsWith("http://") ? new URL(file) : null;
+      return new jarInputStreamProvider()
+      {
+        @Override
+        public JarInputStream getJarInputStream() throws IOException
+        {
+          InputStream is = bytes != null ? new ByteArrayInputStream(bytes)
+                  : (url != null ? url.openStream()
+                          : new FileInputStream(file));
+          return new JarInputStream(is);
+        }
+
+        @Override
+        public File getFile()
+        {
+          return jarFile;
+        }
+
+        @Override
+        public String getFilename()
+        {
+          return file;
+        }
+      };
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+      return null;
+    }
+  }
 
   /**
    * Recover jalview session from a jalview project archive. Caller may
@@ -2896,13 +2935,20 @@ public class Jalview2XML
     AlignFrame af = null, _af = null;
     IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
     Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
-    final String file = jprovider.getFilename();
+    String fileName = jprovider.getFilename();
+    File file = jprovider.getFile();
+    List<AlignFrame> alignFrames = new ArrayList<>();
+
     try
     {
       JarInputStream jin = null;
       JarEntry jarentry = null;
       int entryCount = 1;
 
+
+      // Look for all the entry names ending with ".xml"
+      // This includes all panels and at least one frame.
+//      Platform.timeCheck(null, Platform.TIME_MARK);
       do
       {
         jin = jprovider.getJarInputStream();
@@ -2910,9 +2956,27 @@ public class Jalview2XML
         {
           jarentry = jin.getNextJarEntry();
         }
+        String name = (jarentry == null ? null : jarentry.getName());
 
-        if (jarentry != null && jarentry.getName().endsWith(".xml"))
+//        System.out.println("Jalview2XML opening " + name);
+        if (name != null && name.endsWith(".xml"))
         {
+
+          // DataSet for.... is read last.
+          
+          
+          // The question here is what to do with the two
+          // .xml files in the jvp file.
+          // Some number of them, "...Dataset for...", will be the
+          // Only AlignPanels and will have Viewport.
+          // One or more will be the source data, with the DBRefs.
+          //
+          // JVP file writing (above) ensures tha the AlignPanels are written
+          // first, then all relevant datasets (which are
+          // Jalview.datamodel.Alignment).
+          //
+
+//          Platform.timeCheck("Jalview2XML JAXB " + name, Platform.TIME_MARK);
           JAXBContext jc = JAXBContext
                   .newInstance("jalview.xml.binding.jalview");
           XMLStreamReader streamReader = XMLInputFactory.newInstance()
@@ -2920,14 +2984,25 @@ public class Jalview2XML
           javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
           JAXBElement<JalviewModel> jbe = um
                   .unmarshal(streamReader, JalviewModel.class);
-          JalviewModel object = jbe.getValue();
+          JalviewModel model = jbe.getValue();
 
           if (true) // !skipViewport(object))
           {
-            _af = loadFromObject(object, file, true, jprovider);
-            if (_af != null && object.getViewport().size() > 0)
-            // getJalviewModelSequence().getViewportCount() > 0)
+            // Q: Do we have to load from the model, even if it
+            // does not have a viewport, could we discover that early on?
+            // Q: Do we need to load this object?
+            _af = loadFromObject(model, fileName, file, true, jprovider);
+//            Platform.timeCheck("Jalview2XML.loadFromObject",
+            // Platform.TIME_MARK);
+
+            if (_af != null)
+            {
+              alignFrames.add(_af);
+            }
+            if (_af != null && model.getViewport().size() > 0)
             {
+
+              // That is, this is one of the AlignmentPanel models
               if (af == null)
               {
                 // store a reference to the first view
@@ -2959,7 +3034,7 @@ public class Jalview2XML
     } catch (IOException ex)
     {
       ex.printStackTrace();
-      errorMessage = "Couldn't locate Jalview XML file : " + file;
+      errorMessage = "Couldn't locate Jalview XML file : " + fileName;
       System.err.println(
               "Exception whilst loading jalview XML file : " + ex + "\n");
     } catch (Exception ex)
@@ -2970,9 +3045,9 @@ public class Jalview2XML
       {
         // used to attempt to parse as V1 castor-generated xml
       }
-      if (Desktop.instance != null)
+      if (Desktop.getInstance() != null)
       {
-        Desktop.instance.stopLoading();
+        Desktop.getInstance().stopLoading();
       }
       if (af != null)
       {
@@ -2989,6 +3064,13 @@ public class Jalview2XML
       errorMessage = "Out of memory loading jalview XML file";
       System.err.println("Out of memory whilst loading jalview XML file");
       e.printStackTrace();
+    } finally
+    {
+      for (AlignFrame alf : alignFrames)
+      {
+        alf.alignPanel.setHoldRepaint(false);
+      }
+
     }
 
     /*
@@ -3000,7 +3082,7 @@ public class Jalview2XML
      */
     for (AlignFrame fr : gatherToThisFrame.values())
     {
-      Desktop.instance.gatherViews(fr);
+      Desktop.getInstance().gatherViews(fr);
     }
 
     restoreSplitFrames();
@@ -3008,8 +3090,7 @@ public class Jalview2XML
     {
       if (ds.getCodonFrames() != null)
       {
-        StructureSelectionManager
-                .getStructureSelectionManager(Desktop.instance)
+        Desktop.getStructureSelectionManager()
                 .registerMappings(ds.getCodonFrames());
       }
     }
@@ -3018,9 +3099,9 @@ public class Jalview2XML
       reportErrors();
     }
 
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
-      Desktop.instance.stopLoading();
+      Desktop.getInstance().stopLoading();
     }
 
     return af;
@@ -3101,7 +3182,7 @@ public class Jalview2XML
      */
     for (SplitFrame sf : gatherTo)
     {
-      Desktop.instance.gatherViews(sf);
+      Desktop.getInstance().gatherViews(sf);
     }
 
     splitFrameCandidates.clear();
@@ -3160,7 +3241,7 @@ public class Jalview2XML
           @Override
           public void run()
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     finalErrorMessage,
                     "Error " + (saving ? "saving" : "loading")
                             + " Jalview file",
@@ -3221,25 +3302,25 @@ public class Jalview2XML
    * @param prefix
    *          a prefix for the temporary file name, must be at least three
    *          characters long
-   * @param suffixModel
+   * @param origFile
    *          null or original file - so new file can be given the same suffix
    *          as the old one
    * @return
    */
   protected String copyJarEntry(jarInputStreamProvider jprovider,
-          String jarEntryName, String prefix, String suffixModel)
+          String jarEntryName, String prefix, String origFile)
   {
     BufferedReader in = null;
     PrintWriter out = null;
     String suffix = ".tmp";
-    if (suffixModel == null)
+    if (origFile == null)
     {
-      suffixModel = jarEntryName;
+      origFile = jarEntryName;
     }
-    int sfpos = suffixModel.lastIndexOf(".");
-    if (sfpos > -1 && sfpos < (suffixModel.length() - 1))
+    int sfpos = origFile.lastIndexOf(".");
+    if (sfpos > -1 && sfpos < (origFile.length() - 3))
     {
-      suffix = "." + suffixModel.substring(sfpos + 1);
+      suffix = "." + origFile.substring(sfpos + 1);
     }
     try
     {
@@ -3319,24 +3400,28 @@ public class Jalview2XML
   }
 
   /**
-   * Load alignment frame from jalview XML DOM object
+   * Load alignment frame from jalview XML DOM object. For a DOM object that
+   * includes one or more Viewport elements (one with a title that does NOT
+   * contain "Dataset for"), create the frame.
    * 
    * @param jalviewModel
    *          DOM
-   * @param file
+   * @param fileName
    *          filename source string
+   * @param file 
    * @param loadTreesAndStructures
    *          when false only create Viewport
    * @param jprovider
    *          data source provider
    * @return alignment frame created from view stored in DOM
    */
-  AlignFrame loadFromObject(JalviewModel jalviewModel, String file,
-          boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
+  AlignFrame loadFromObject(JalviewModel jalviewModel, String fileName,
+          File file, boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
   {
     SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet().get(0);
     List<Sequence> vamsasSeqs = vamsasSet.getSequence();
 
+
     // JalviewModelSequence jms = object.getJalviewModelSequence();
 
     // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
@@ -3632,8 +3717,7 @@ public class Jalview2XML
             {
               entry.setProperty(prop.getName(), prop.getValue());
             }
-            StructureSelectionManager
-                    .getStructureSelectionManager(Desktop.instance)
+            Desktop.getStructureSelectionManager()
                     .registerPDBEntry(entry);
             // adds PDBEntry to datasequence's set (since Jalview 2.10)
             if (al.getSequenceAt(i).getDatasetSequence() != null)
@@ -3657,7 +3741,6 @@ public class Jalview2XML
         {
           loadHmmerProfile(jprovider, hmmJarFile, al.getSequenceAt(i));
         }
-
       }
     } // end !multipleview
 
@@ -3795,6 +3878,7 @@ public class Jalview2XML
             }
           }
         }
+        // create the new AlignmentAnnotation
         jalview.datamodel.AlignmentAnnotation jaa = null;
 
         if (annotation.isGraph())
@@ -3831,6 +3915,7 @@ public class Jalview2XML
           jaa._linecolour = firstColour;
         }
         // register new annotation
+        // Annotation graphs such as Conservation will not have id.
         if (annotation.getId() != null)
         {
           annotationIds.put(annotation.getId(), jaa);
@@ -4042,8 +4127,6 @@ public class Jalview2XML
     // ///////////////////////////////
     // LOAD VIEWPORT
 
-    AlignFrame af = null;
-    AlignViewport av = null;
     // now check to see if we really need to create a new viewport.
     if (multipleView && viewportsAdded.size() == 0)
     {
@@ -4082,8 +4165,9 @@ public class Jalview2XML
     boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1",
             jalviewModel.getVersion());
 
+    AlignFrame af = null;
     AlignmentPanel ap = null;
-    boolean isnewview = true;
+    AlignViewport av = null;
     if (viewId != null)
     {
       // Check to see if this alignment already has a view id == viewId
@@ -4093,25 +4177,27 @@ public class Jalview2XML
       {
         for (int v = 0; v < views.length; v++)
         {
-          if (views[v].av.getViewId().equalsIgnoreCase(viewId))
+          ap = views[v];
+          av = ap.av;
+          if (av.getViewId().equalsIgnoreCase(viewId))
           {
             // recover the existing alignpanel, alignframe, viewport
-            af = views[v].alignFrame;
-            av = views[v].av;
-            ap = views[v];
+            af = ap.alignFrame;
+            break;
             // TODO: could even skip resetting view settings if we don't want to
             // change the local settings from other jalview processes
-            isnewview = false;
           }
         }
       }
     }
 
-    if (isnewview)
+    if (af == null)
     {
-      af = loadViewport(file, jseqs, hiddenSeqs, al, jalviewModel, view,
+      af = loadViewport(fileName, file, jseqs, hiddenSeqs, al, jalviewModel, view,
               uniqueSeqSetId, viewId, autoAlan);
       av = af.getViewport();
+      // note that this only retrieves the most recently accessed
+      // tab of an AlignFrame.
       ap = af.alignPanel;
     }
 
@@ -4120,14 +4206,61 @@ public class Jalview2XML
      * 
      * Not done if flag is false (when this method is used for New View)
      */
+    final AlignFrame af0 = af;
+    final AlignViewport av0 = av;
+    final AlignmentPanel ap0 = ap;
+//    Platform.timeCheck("Jalview2XML.loadFromObject-beforetree",
+//            Platform.TIME_MARK);
     if (loadTreesAndStructures)
     {
-      loadTrees(jalviewModel, view, af, av, ap);
-      loadPCAViewers(jalviewModel, ap);
-      loadPDBStructures(jprovider, jseqs, af, ap);
-      loadRnaViewers(jprovider, jseqs, ap);
+      if (!jalviewModel.getTree().isEmpty())
+      {
+        SwingUtilities.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+//            Platform.timeCheck(null, Platform.TIME_MARK);
+            loadTrees(jalviewModel, view, af0, av0, ap0);
+//            Platform.timeCheck("Jalview2XML.loadTrees", Platform.TIME_MARK);
+          }
+        });
+      }
+      if (!jalviewModel.getPcaViewer().isEmpty())
+      {
+        SwingUtilities.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+//            Platform.timeCheck(null, Platform.TIME_MARK);
+            loadPCAViewers(jalviewModel, ap0);
+//            Platform.timeCheck("Jalview2XML.loadPCA", Platform.TIME_MARK);
+          }
+        });
+      }
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+//          Platform.timeCheck(null, Platform.TIME_MARK);
+          loadPDBStructures(jprovider, jseqs, af0, ap0);
+//          Platform.timeCheck("Jalview2XML.loadPDB", Platform.TIME_MARK);
+        }
+      });
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          loadRnaViewers(jprovider, jseqs, ap0);
+        }
+      });
     }
     // and finally return.
+    // but do not set holdRepaint true just yet, because this could be the
+    // initial frame with just its dataset.
     return af;
   }
 
@@ -4169,7 +4302,7 @@ public class Jalview2XML
    * @param jseqs
    * @param ap
    */
-  private void loadRnaViewers(jarInputStreamProvider jprovider,
+  protected void loadRnaViewers(jarInputStreamProvider jprovider,
           List<JSeq> jseqs, AlignmentPanel ap)
   {
     /*
@@ -4279,6 +4412,12 @@ public class Jalview2XML
                   tree.getTitle(), safeInt(tree.getWidth()),
                   safeInt(tree.getHeight()), safeInt(tree.getXpos()),
                   safeInt(tree.getYpos()));
+          if (tp == null)
+          {
+            warn("There was a problem recovering stored Newick tree: \n"
+                    + tree.getNewick());
+            continue;
+          }
           if (tree.getId() != null)
           {
             // perhaps bind the tree id to something ?
@@ -4299,13 +4438,6 @@ public class Jalview2XML
           tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
         }
         tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews());
-        if (tp == null)
-        {
-          warn("There was a problem recovering stored Newick tree: \n"
-                  + tree.getNewick());
-          continue;
-        }
-
         tp.fitToWindow.setState(safeBoolean(tree.isFitToWindow()));
         tp.fitToWindow_actionPerformed(null);
 
@@ -4388,7 +4520,7 @@ public class Jalview2XML
             int height = safeInt(structureState.getHeight());
 
             // Probably don't need to do this anymore...
-            // Desktop.desktop.getComponentAt(x, y);
+            // Desktop.getDesktop().getComponentAt(x, y);
             // TODO: NOW: check that this recovers the PDB file correctly.
             String pdbFile = loadPDBFile(jprovider, pdbid.getId(),
                     pdbid.getFile());
@@ -4630,7 +4762,8 @@ public class Jalview2XML
           String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
           filedat = oldFiles.get(new File(reformatedOldFilename));
         }
-        newFileLoc.append(Platform.escapeBackslashes(filedat.getFilePath()));
+        newFileLoc
+                .append(Platform.escapeBackslashes(filedat.getFilePath()));
         pdbfilenames.add(filedat.getFilePath());
         pdbids.add(filedat.getPdbId());
         seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
@@ -4707,9 +4840,11 @@ public class Jalview2XML
     final AlignFrame alf = af;
     final Rectangle rect = new Rectangle(svattrib.getX(), svattrib.getY(),
             svattrib.getWidth(), svattrib.getHeight());
-    try
-    {
-      javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+    
+    // BH again was invokeAndWait
+    // try
+    // {
+      javax.swing.SwingUtilities.invokeLater(new Runnable()
       {
         @Override
         public void run()
@@ -4736,14 +4871,14 @@ public class Jalview2XML
           }
         }
       });
-    } catch (InvocationTargetException ex)
-    {
-      warn("Unexpected error when opening Jmol view.", ex);
-
-    } catch (InterruptedException e)
-    {
-      // e.printStackTrace();
-    }
+    // } catch (InvocationTargetException ex)
+    // {
+    // warn("Unexpected error when opening Jmol view.", ex);
+    //
+    // } catch (InterruptedException e)
+    // {
+    // // e.printStackTrace();
+    // }
 
   }
 
@@ -4872,7 +5007,7 @@ public class Jalview2XML
     {
       try
       {
-        frames = Desktop.desktop.getAllFrames();
+        frames = Desktop.getDesktopPane().getAllFrames();
       } catch (ArrayIndexOutOfBoundsException e)
       {
         // occasional No such child exceptions are thrown here...
@@ -4942,27 +5077,28 @@ public class Jalview2XML
     }
   }
 
-  AlignFrame loadViewport(String file, List<JSeq> JSEQ,
-          List<SequenceI> hiddenSeqs, AlignmentI al,
-          JalviewModel jm, Viewport view, String uniqueSeqSetId,
-          String viewId, List<JvAnnotRow> autoAlan)
+  AlignFrame loadViewport(String fileName, File file, List<JSeq> JSEQ,
+          List<SequenceI> hiddenSeqs, AlignmentI al, JalviewModel jm,
+          Viewport view, String uniqueSeqSetId, String viewId,
+          List<JvAnnotRow> autoAlan)
   {
     AlignFrame af = null;
     af = new AlignFrame(al, safeInt(view.getWidth()),
-            safeInt(view.getHeight()), uniqueSeqSetId, viewId) 
-//    {
-//     
-//     @Override
-//     protected void processKeyEvent(java.awt.event.KeyEvent e) {
-//             System.out.println("Jalview2XML   AF " + e);
-//             super.processKeyEvent(e);
-//             
-//     }
-//     
-//    }
+            safeInt(view.getHeight()), uniqueSeqSetId, viewId)
+    // {
+    //
+    // @Override
+    // protected void processKeyEvent(java.awt.event.KeyEvent e) {
+    // System.out.println("Jalview2XML AF " + e);
+    // super.processKeyEvent(e);
+    //
+    // }
+    //
+    // }
     ;
-
-    af.setFileName(file, FileFormat.Jalview);
+    af.alignPanel.setHoldRepaint(true);
+    af.setFile(fileName, file, null, FileFormat.Jalview);
+    af.setFileObject(jarFile); // BH 2019 JAL-3436
 
     final AlignViewport viewport = af.getViewport();
     for (int i = 0; i < JSEQ.size(); i++)
@@ -5037,9 +5173,8 @@ public class Jalview2XML
 
     viewport.setColourText(safeBoolean(view.isShowColourText()));
 
-    viewport
-            .setConservationSelected(
-                    safeBoolean(view.isConservationSelected()));
+    viewport.setConservationSelected(
+            safeBoolean(view.isConservationSelected()));
     viewport.setIncrement(safeInt(view.getConsThreshold()));
     viewport.setShowJVSuffix(safeBoolean(view.isShowFullId()));
     viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds()));
@@ -5070,8 +5205,18 @@ public class Jalview2XML
       viewport.setViewName(view.getViewName());
       af.setInitialTabVisible();
     }
-    af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()),
-            safeInt(view.getWidth()), safeInt(view.getHeight()));
+    int x = safeInt(view.getXpos());
+    int y = safeInt(view.getYpos());
+    int w = safeInt(view.getWidth());
+    int h = safeInt(view.getHeight());
+    // // BH we cannot let the title bar go off the top
+    // if (Platform.isJS())
+    // {
+    // x = Math.max(50 - w, x);
+    // y = Math.max(0, y);
+    // }
+
+    af.setBounds(x, y, w, h);
     // startSeq set in af.alignPanel.updateLayout below
     af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
@@ -5115,9 +5260,8 @@ public class Jalview2XML
     af.changeColour(cs);
     viewport.setColourAppliesToAllGroups(true);
 
-    viewport
-            .setShowSequenceFeatures(
-                    safeBoolean(view.isShowSequenceFeatures()));
+    viewport.setShowSequenceFeatures(
+            safeBoolean(view.isShowSequenceFeatures()));
 
     viewport.setCentreColumnLabels(view.isCentreColumnLabels());
     viewport.setIgnoreGapsConsensus(view.isIgnoreGapsinConsensus(), null);
@@ -5137,17 +5281,17 @@ public class Jalview2XML
     // recover feature settings
     if (jm.getFeatureSettings() != null)
     {
-      FeatureRendererModel fr = af.alignPanel.getSeqPanel().seqCanvas
+      FeatureRenderer fr = af.alignPanel.getSeqPanel().seqCanvas
               .getFeatureRenderer();
       FeaturesDisplayed fdi;
       viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
-      String[] renderOrder = new String[jm.getFeatureSettings()
-              .getSetting().size()];
+      String[] renderOrder = new String[jm.getFeatureSettings().getSetting()
+              .size()];
       Map<String, FeatureColourI> featureColours = new Hashtable<>();
       Map<String, Float> featureOrder = new Hashtable<>();
 
-      for (int fs = 0; fs < jm.getFeatureSettings()
-              .getSetting().size(); fs++)
+      for (int fs = 0; fs < jm.getFeatureSettings().getSetting()
+              .size(); fs++)
       {
         Setting setting = jm.getFeatureSettings().getSetting().get(fs);
         String featureType = setting.getType();
@@ -5159,8 +5303,8 @@ public class Jalview2XML
                 .getMatcherSet();
         if (filters != null)
         {
-          FeatureMatcherSetI filter = Jalview2XML
-                  .parseFilter(featureType, filters);
+          FeatureMatcherSetI filter = Jalview2XML.parseFilter(featureType,
+                  filters);
           if (!filter.isEmpty())
           {
             fr.setFeatureFilter(featureType, filter);
@@ -5192,8 +5336,7 @@ public class Jalview2XML
           float max = setting.getMax() == null ? 1f
                   : setting.getMax().floatValue();
           FeatureColourI gc = new FeatureColour(maxColour, minColour,
-                  maxColour,
-                  noValueColour, min, max);
+                  maxColour, noValueColour, min, max);
           if (setting.getAttributeName().size() > 0)
           {
             gc.setAttributeName(setting.getAttributeName().toArray(
@@ -5227,8 +5370,7 @@ public class Jalview2XML
         }
         else
         {
-          featureColours.put(featureType,
-                  new FeatureColour(maxColour));
+          featureColours.put(featureType, new FeatureColour(maxColour));
         }
         renderOrder[fs] = featureType;
         if (setting.getOrder() != null)
@@ -5296,8 +5438,9 @@ public class Jalview2XML
     String complementaryViewId = view.getComplementId();
     if (complementaryViewId == null)
     {
-      Desktop.addInternalFrame(af, view.getTitle(),
+      Dimension dim = Platform.getDimIfEmbedded(af,
               safeInt(view.getWidth()), safeInt(view.getHeight()));
+      Desktop.addInternalFrame(af, view.getTitle(), dim.width, dim.height);
       // recompute any autoannotation
       af.alignPanel.updateAnnotation(false, true);
       reorderAutoannotation(af, al, autoAlan);
@@ -5564,7 +5707,7 @@ public class Jalview2XML
     return false;
   }
 
-  public void addToSkipList(AlignFrame af)
+  protected void addToSkipList(AlignFrame af)
   {
     if (skipList == null)
     {
@@ -5573,7 +5716,7 @@ public class Jalview2XML
     skipList.put(af.getViewport().getSequenceSetId(), af);
   }
 
-  public void clearSkipList()
+  protected void clearSkipList()
   {
     if (skipList != null)
     {
@@ -5642,8 +5785,8 @@ public class Jalview2XML
       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
       dseqs.copyInto(dsseqs);
       ds = new jalview.datamodel.Alignment(dsseqs);
-      debug("Created new dataset " + vamsasSet.getDatasetId()
-              + " for alignment " + System.identityHashCode(al));
+//      debug("Jalview2XML Created new dataset " + vamsasSet.getDatasetId()
+//              + " for alignment " + System.identityHashCode(al));
       addDatasetRef(vamsasSet.getDatasetId(), ds);
     }
     // set the dataset for the newly imported alignment.
@@ -6069,9 +6212,11 @@ public class Jalview2XML
 
     viewportsAdded.clear();
 
-    AlignFrame af = loadFromObject(jm, null, false, null);
+    AlignFrame af = loadFromObject(jm, null, null, false, null);
     af.getAlignPanels().clear();
     af.closeMenuItem_actionPerformed(true);
+    af.alignPanel.setHoldRepaint(false);
+    this.jarFile = null;
 
     /*
      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
@@ -6430,8 +6575,10 @@ public class Jalview2XML
                   axis.getXPos(), axis.getYPos(), axis.getZPos());
         }
 
+        Dimension dim = Platform.getDimIfEmbedded(panel, 475, 450);
         Desktop.addInternalFrame(panel, MessageManager.formatMessage(
-                "label.calc_title", "PCA", modelName), 475, 450);
+                "label.calc_title", "PCA", modelName), dim.width,
+                dim.height);
       }
     } catch (Exception ex)
     {
index a37882f..7c8c9a6 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.rest;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.httpserver.AbstractRequestHandler;
 
 import java.io.IOException;
@@ -30,20 +32,16 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 /**
- * A simple handler to process (or delegate) HTTP requests on /jalview/rest
+ * A simple handler to process (or delegate) HTTP requests on /jalview/rest.
  */
 public class RestHandler extends AbstractRequestHandler
+        implements ApplicationSingletonI
 {
   private static final String MY_PATH = "rest";
 
   private static final String MY_NAME = "Rest";
 
   /**
-   * Singleton instance of this class
-   */
-  private static RestHandler instance = null;
-
-  /**
    * Returns the singleton instance of this class
    * 
    * @return
@@ -53,12 +51,8 @@ public class RestHandler extends AbstractRequestHandler
   {
     synchronized (RestHandler.class)
     {
-      if (instance == null)
-      {
-        instance = new RestHandler();
-      }
+      return (RestHandler) ApplicationSingletonProvider.getInstance(RestHandler.class);
     }
-    return instance;
   }
 
   /**
index d31fbba..0ff7c6e 100644 (file)
 package jalview.schemes;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
+import jalview.util.ColorUtils;
 
+import java.awt.Color;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-public class ColourSchemes
+public class ColourSchemes implements ApplicationSingletonI
 {
-  /*
-   * singleton instance of this class
-   */
-  private static ColourSchemes instance = new ColourSchemes();
-
-  /*
-   * a map from scheme name (lower-cased) to an instance of it
-   */
-  private Map<String, ColourSchemeI> schemes;
 
   /**
    * Returns the singleton instance of this class
@@ -47,7 +42,8 @@ public class ColourSchemes
    */
   public static ColourSchemes getInstance()
   {
-    return instance;
+    return (ColourSchemes) ApplicationSingletonProvider
+            .getInstance(ColourSchemes.class);
   }
 
   private ColourSchemes()
@@ -56,6 +52,51 @@ public class ColourSchemes
   }
 
   /**
+   * ColourSchemeProperty "static"
+   */
+  public Color[] rnaHelices = null;
+
+  /**
+   * delete the existing cached RNA helices colours
+   */
+  public static void resetRnaHelicesShading()
+  {
+    getInstance().rnaHelices = null;
+  }
+
+  public static void initRnaHelicesShading(int n)
+  {
+    int i = 0;
+    ColourSchemes j = getInstance();
+
+    if (j.rnaHelices == null)
+    {
+      j.rnaHelices = new Color[n + 1];
+    }
+    else if (j.rnaHelices != null && j.rnaHelices.length <= n)
+    {
+      Color[] t = new Color[n + 1];
+      System.arraycopy(j.rnaHelices, 0, t, 0, j.rnaHelices.length);
+      i = j.rnaHelices.length;
+      j.rnaHelices = t;
+    }
+    else
+    {
+      return;
+    }
+    // Generate random colors and store
+    for (; i <= n; i++)
+    {
+      j.rnaHelices[i] = ColorUtils.generateRandomColor(Color.white);
+    }
+  }
+
+  /**
+   * a map from scheme name (lower-cased) to an instance of it
+   */
+  private Map<String, ColourSchemeI> schemes;
+
+  /**
    * Loads an instance of each standard or user-defined colour scheme
    * 
    * @return
index 9662fee..b5672ab 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.structure;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.PDBEntry.Type;
 
@@ -30,27 +32,39 @@ import jalview.datamodel.PDBEntry.Type;
  * @author tcofoegbu
  *
  */
-public class StructureImportSettings
+public class StructureImportSettings implements ApplicationSingletonI
 {
+
+  private StructureImportSettings()
+  {
+    // private singleton
+  }
+
+  private static StructureImportSettings getInstance()
+  {
+    return (StructureImportSettings) ApplicationSingletonProvider
+            .getInstance(StructureImportSettings.class);
+  }
+
   /**
    * set to true to add derived sequence annotations (temp factor read from
    * file, or computed secondary structure) to the alignment
    */
-  private static boolean visibleChainAnnotation = false;
+  private boolean visibleChainAnnotation = false;
 
   /**
    * Set true to predict secondary structure (using JMol for protein, Annotate3D
    * for RNA)
    */
-  private static boolean processSecStr = false;
+  private boolean processSecStr = false;
 
   /**
    * Set true (with predictSecondaryStructure=true) to predict secondary
    * structure using an external service (currently Annotate3D for RNA only)
    */
-  private static boolean externalSecondaryStructure = false;
+  private boolean externalSecondaryStructure = false;
 
-  private static boolean showSeqFeatures = true;
+  private boolean showSeqFeatures = true;
 
   public enum StructureParser
   {
@@ -61,92 +75,93 @@ public class StructureImportSettings
    * Determines the default file format for structure files to be downloaded
    * from the PDB sequence fetcher. Possible options include: PDB|mmCIF
    */
-  private static PDBEntry.Type defaultStructureFileFormat = Type.PDB;
+  private PDBEntry.Type defaultStructureFileFormat = Type.PDB;
 
   /**
    * Determines the parser used for parsing PDB format file. Possible options
    * are : JMolParser|JalveiwParser
    */
-  private static StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER;
+  private StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER;
 
   public static void addSettings(boolean addAlignmentAnnotations,
           boolean processSecStr, boolean externalSecStr)
   {
-    StructureImportSettings.visibleChainAnnotation = addAlignmentAnnotations;
-    StructureImportSettings.processSecStr = processSecStr;
-    StructureImportSettings.externalSecondaryStructure = externalSecStr;
-    StructureImportSettings.showSeqFeatures = true;
+    StructureImportSettings s = getInstance();
+    s.visibleChainAnnotation = addAlignmentAnnotations;
+    s.processSecStr = processSecStr;
+    s.externalSecondaryStructure = externalSecStr;
+    s.showSeqFeatures = true;
   }
 
   public static boolean isVisibleChainAnnotation()
   {
-    return visibleChainAnnotation;
+    return getInstance().visibleChainAnnotation;
   }
 
   public static void setVisibleChainAnnotation(
           boolean visibleChainAnnotation)
   {
-    StructureImportSettings.visibleChainAnnotation = visibleChainAnnotation;
+    getInstance().visibleChainAnnotation = visibleChainAnnotation;
   }
 
   public static boolean isProcessSecondaryStructure()
   {
-    return processSecStr;
+    return getInstance().processSecStr;
   }
 
   public static void setProcessSecondaryStructure(
           boolean processSecondaryStructure)
   {
-    StructureImportSettings.processSecStr = processSecondaryStructure;
+    getInstance().processSecStr = processSecondaryStructure;
   }
 
   public static boolean isExternalSecondaryStructure()
   {
-    return externalSecondaryStructure;
+    return getInstance().externalSecondaryStructure;
   }
 
   public static void setExternalSecondaryStructure(
           boolean externalSecondaryStructure)
   {
-    StructureImportSettings.externalSecondaryStructure = externalSecondaryStructure;
+    getInstance().externalSecondaryStructure = externalSecondaryStructure;
   }
 
   public static boolean isShowSeqFeatures()
   {
-    return showSeqFeatures;
+    return getInstance().showSeqFeatures;
   }
 
   public static void setShowSeqFeatures(boolean showSeqFeatures)
   {
-    StructureImportSettings.showSeqFeatures = showSeqFeatures;
+    getInstance().showSeqFeatures = showSeqFeatures;
   }
 
   public static PDBEntry.Type getDefaultStructureFileFormat()
   {
-    return defaultStructureFileFormat;
+    return getInstance().defaultStructureFileFormat;
   }
 
   public static void setDefaultStructureFileFormat(
           String defaultStructureFileFormat)
   {
-    StructureImportSettings.defaultStructureFileFormat = PDBEntry.Type
+    getInstance().defaultStructureFileFormat = PDBEntry.Type
             .valueOf(defaultStructureFileFormat.toUpperCase());
   }
 
   public static String getDefaultPDBFileParser()
   {
-    return defaultPDBFileParser.toString();
+    return getInstance().defaultPDBFileParser.toString();
   }
 
   public static void setDefaultPDBFileParser(
           StructureParser defaultPDBFileParser)
   {
-    StructureImportSettings.defaultPDBFileParser = defaultPDBFileParser;
+    getInstance().defaultPDBFileParser = defaultPDBFileParser;
   }
 
   public static void setDefaultPDBFileParser(String defaultPDBFileParser)
   {
-    StructureImportSettings.defaultPDBFileParser = StructureParser
+    getInstance().defaultPDBFileParser = StructureParser
             .valueOf(defaultPDBFileParser.toUpperCase());
   }
 
index 798b07a..f8048cd 100644 (file)
@@ -22,6 +22,9 @@ package jalview.structure;
 
 import jalview.analysis.AlignSeq;
 import jalview.api.StructureSelectionManagerProvider;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+import jalview.bin.Cache;
 import jalview.commands.CommandI;
 import jalview.commands.EditCommand;
 import jalview.commands.OrderCommand;
@@ -61,12 +64,10 @@ import mc_view.Atom;
 import mc_view.PDBChain;
 import mc_view.PDBfile;
 
-public class StructureSelectionManager
+public class StructureSelectionManager implements ApplicationSingletonI
 {
   public final static String NEWLINE = System.lineSeparator();
 
-  static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
-
   private List<StructureMapping> mappings = new ArrayList<>();
 
   private boolean processSecondaryStructure = false;
@@ -84,6 +85,67 @@ public class StructureSelectionManager
 
   private List<SelectionListener> sel_listeners = new ArrayList<>();
 
+  /*
+   * instances of this class scoped by some context class
+   */
+  private IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> selectionManagers;
+
+  /**
+   * Answers an instance of this class for the current application (Java or JS
+   * 'applet') scope
+   * 
+   * @return
+   */
+  private static StructureSelectionManager getInstance()
+  {
+    return (StructureSelectionManager) ApplicationSingletonProvider
+            .getInstance(StructureSelectionManager.class);
+  }
+
+  /**
+   * Private constructor as all 'singleton' instances are managed here or by
+   * ApplicationSingletonProvider
+   */
+  private StructureSelectionManager()
+  {
+    selectionManagers = new IdentityHashMap<>();
+  }
+
+  /**
+   * Answers an instance of this class for the current application (Java or JS
+   * 'applet') scope, and scoped to the specified context
+   * 
+   * @param context
+   * @return
+   */
+  public static StructureSelectionManager getStructureSelectionManager(
+          StructureSelectionManagerProvider context)
+  {
+    return getInstance().getInstanceForContext(context);
+  }
+
+  /**
+   * Answers an instance of this class scoped to the given context. The instance
+   * is created on the first request for the context, thereafter the same
+   * instance is returned. Note that the context may be null (this is the case
+   * when running headless without a Desktop).
+   * 
+   * @param context
+   * @return
+   */
+  StructureSelectionManager getInstanceForContext(
+          StructureSelectionManagerProvider context)
+  {
+    StructureSelectionManager instance = selectionManagers.get(context);
+    if (instance == null)
+    {
+      instance = new StructureSelectionManager();
+      selectionManagers.put(context, instance);
+    }
+    return instance;
+  }
+
+
   /**
    * @return true if will try to use external services for processing secondary
    *         structure
@@ -198,49 +260,6 @@ public class StructureSelectionManager
             || pdbIdFileName.containsKey(idOrFile);
   }
 
-  private static StructureSelectionManager nullProvider = null;
-
-  public static StructureSelectionManager getStructureSelectionManager(
-          StructureSelectionManagerProvider context)
-  {
-    if (context == null)
-    {
-      if (nullProvider == null)
-      {
-        if (instances != null)
-        {
-          throw new Error(MessageManager.getString(
-                  "error.implementation_error_structure_selection_manager_null"),
-                  new NullPointerException(MessageManager
-                          .getString("exception.ssm_context_is_null")));
-        }
-        else
-        {
-          nullProvider = new StructureSelectionManager();
-        }
-        return nullProvider;
-      }
-    }
-    if (instances == null)
-    {
-      instances = new java.util.IdentityHashMap<>();
-    }
-    StructureSelectionManager instance = instances.get(context);
-    if (instance == null)
-    {
-      if (nullProvider != null)
-      {
-        instance = nullProvider;
-      }
-      else
-      {
-        instance = new StructureSelectionManager();
-      }
-      instances.put(context, instance);
-    }
-    return instance;
-  }
-
   /**
    * flag controlling whether SeqMappings are relayed from received sequence
    * mouse over events to other sequences
@@ -270,7 +289,7 @@ public class StructureSelectionManager
     return relaySeqMappings;
   }
 
-  Vector listeners = new Vector();
+  Vector<Object> listeners = new Vector<>();
 
   /**
    * register a listener for alignment sequence mouseover events
@@ -308,6 +327,8 @@ public class StructureSelectionManager
    * Import structure data and register a structure mapping for broadcasting
    * colouring, mouseovers and selection events (convenience wrapper).
    * 
+   * This is the standard entry point.
+   * 
    * @param sequence
    *          - one or more sequences to be mapped to pdbFile
    * @param targetChains
@@ -332,8 +353,11 @@ public class StructureSelectionManager
    * broadcasting colouring, mouseovers and selection events (convenience
    * wrapper).
    * 
+   * 
+   * 
    * @param forStructureView
-   *          when true, record the mapping for use in mouseOvers
+   *          when true (testng only), record the mapping for use in mouseOvers
+   *          (testng only)
    * @param sequence
    *          - one or more sequences to be mapped to pdbFile
    * @param targetChains
@@ -377,7 +401,7 @@ public class StructureSelectionManager
    *          mapping operation
    * @return null or the structure data parsed as a pdb file
    */
-  synchronized public StructureFile computeMapping(
+  synchronized private StructureFile computeMapping(
           boolean forStructureView, SequenceI[] sequenceArray,
           String[] targetChainIds, String pdbFile, DataSourceType sourceType,
           IProgressIndicator progress)
@@ -430,7 +454,8 @@ public class StructureSelectionManager
     } catch (SiftsException e)
     {
       isMapUsingSIFTs = false;
-      e.printStackTrace();
+      Cache.log.error("SIFTS mapping failed", e);
+      Cache.log.error("Falling back on Needleman & Wunsch alignment");
       siftsClient = null;
     }
 
@@ -651,7 +676,6 @@ public class StructureSelectionManager
         {
           ds = ds.getDatasetSequence();
         }
-        ;
         if (ds.getAnnotation() != null)
         {
           for (AlignmentAnnotation ala : ds.getAnnotation())
@@ -905,9 +929,9 @@ public class StructureSelectionManager
         if (s != null)
         {
           result = s;
-        }
       }
     }
+  }
     return result;
   }
 
@@ -1241,8 +1265,11 @@ public class StructureSelectionManager
   }
 
   /**
-   * Resets this object to its initial state by removing all registered
-   * listeners, codon mappings, PDB file mappings
+   * Reset this object to its initial state by removing all registered
+   * listeners, codon mappings, PDB file mappings.
+   * 
+   * Called only by Desktop and testng.
+   * 
    */
   public void resetAll()
   {
@@ -1280,7 +1307,11 @@ public class StructureSelectionManager
     }
   }
 
-  public void addSelectionListener(SelectionListener selecter)
+  public List<SelectionListener> getListeners() {
+    return sel_listeners;
+  }
+  
+   public void addSelectionListener(SelectionListener selecter)
   {
     if (!sel_listeners.contains(selecter))
     {
@@ -1328,41 +1359,23 @@ public class StructureSelectionManager
         {
           slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
         }
-        ;
+        
       }
     }
   }
 
+  
   /**
-   * release all references associated with this manager provider
+   * Removes the instance associated with this provider
    * 
-   * @param jalviewLite
+   * @param provider
    */
-  public static void release(StructureSelectionManagerProvider jalviewLite)
+
+  public static void release(StructureSelectionManagerProvider provider)
   {
-    // synchronized (instances)
-    {
-      if (instances == null)
-      {
-        return;
-      }
-      StructureSelectionManager mnger = (instances.get(jalviewLite));
-      if (mnger != null)
-      {
-        instances.remove(jalviewLite);
-        try
-        {
-          /* bsoares 2019-03-20 finalize deprecated, no apparent external
-           * resources to close
-           */
-          // mnger.finalize();
-        } catch (Throwable x)
-        {
-        }
-      }
-    }
+    getInstance().selectionManagers.remove(provider);
   }
-
+  
   public void registerPDBEntry(PDBEntry pdbentry)
   {
     if (pdbentry.getFile() != null
index 7dd1a19..7c38ab6 100644 (file)
 
 package jalview.urls;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 /**
  * Holds settings for identifiers.org e.g. url, download location
- * 
- * @author $author$
- * @version $Revision$
  */
-public class IdOrgSettings
+public class IdOrgSettings implements ApplicationSingletonI
 {
-  private static String url;
+  private String url;
+
+  private String location;
+
+  private static IdOrgSettings getInstance()
+  {
+    return (IdOrgSettings) ApplicationSingletonProvider
+            .getInstance(IdOrgSettings.class);
+  }
 
-  private static String location;
+  private IdOrgSettings()
+  {
+    // private singleton
+  }
 
   public static void setUrl(String seturl)
   {
-    url = seturl;
+    getInstance().url = seturl;
   }
 
   public static void setDownloadLocation(String setloc)
   {
-    location = setloc;
+    getInstance().location = setloc;
   }
 
   public static String getUrl()
   {
-    return url;
+    return getInstance().url;
   }
 
   public static String getDownloadLocation()
   {
-    return location;
+    return getInstance().location;
   }
 }
index 07eb23e..e898438 100644 (file)
@@ -118,11 +118,6 @@ private HashMap<String, UrlLink> readIdentifiers(String idFileName)
       }
     } catch (IOException | ParseException e)
     {
-      // unnecessary e.printStackTrace();
-      // Note how in JavaScript we can grab the first bytes from any file reader. 
-      // Typical report here is "NetworkError" because the file does not exist.
-      // "https://." is coming from System.getProperty("user.home"), but this could
-      // be set by the page developer to anything, of course.
       errorMessage = e.toString();
       idData.clear();
     }
index 60129fb..4305f59 100644 (file)
@@ -219,12 +219,15 @@ public class ColorUtils
     colour = colour.trim();
 
     Color col = null;
-    try
-    {
-      int value = Integer.parseInt(colour, 16);
-      col = new Color(value);
-    } catch (NumberFormatException ex)
+    if (StringUtils.isHexString(colour))
     {
+      try
+      {
+        int value = Integer.parseInt(colour, 16);
+        col = new Color(value);
+      } catch (NumberFormatException ex)
+      {
+      }
     }
 
     if (col == null)
index ae0243e..07e8a56 100755 (executable)
@@ -307,6 +307,15 @@ public class DBRefUtils
 
        };
 
+  private static Regex PARSE_REGEX;
+
+  private static Regex getParseRegex()
+  {
+    return (PARSE_REGEX == null ? PARSE_REGEX = Platform.newRegex(
+            "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)")
+            : PARSE_REGEX);
+  }
+
   /**
    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
    * database is PDB.
@@ -334,8 +343,7 @@ public class DBRefUtils
          * Check for PFAM style stockhom PDB accession id citation e.g.
          * "1WRI A; 7-80;"
          */
-        Regex r = new com.stevesoft.pat.Regex(
-                "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)");
+        Regex r = getParseRegex();
         if (r.search(acn.trim()))
         {
           String pdbid = r.stringMatched(1);
index bb94566..2a07616 100644 (file)
@@ -57,8 +57,7 @@ public class MessageManager
       // Locale.setDefault(loc);
       /* Getting messages for GV */
       log.info("Getting messages for lang: " + loc);
-      Control control = Control.getControl(Control.FORMAT_PROPERTIES);
-      rb = ResourceBundle.getBundle("lang.Messages", loc, control);
+      rb = ResourceBundle.getBundle("lang.Messages", Platform.getLocaleOrNone(loc), Control.getControl(Control.FORMAT_PROPERTIES));
       // if (log.isLoggable(Level.FINEST))
       // {
       // // this might take a while, so we only do it if it will be shown
@@ -94,7 +93,7 @@ public class MessageManager
     } catch (Exception e)
     {
       String msg = "I18N missing: " + loc + "\t" + key;
-         logWarning(key, msg);
+    logWarning(key, msg);
     }
     return value;
   }
@@ -169,8 +168,8 @@ public class MessageManager
     } catch (Exception x)
     {
       String msg = "I18N missing key with root " + keyroot + ": " + loc + "\t"
-                         + smkey;
-         logWarning(smkey, msg);
+              + smkey;
+    logWarning(smkey, msg);
     }
     return name;
   }
@@ -183,10 +182,10 @@ public class MessageManager
    */
   private static void logWarning(String key, String msg) 
   {
-       if (!reportedMissing.contains(key))
-       {
+  if (!reportedMissing.contains(key))
+  {
       reportedMissing.add(key);
-         log.info(msg);
-       }
+    log.info(msg);
+  }
   }
 }
index 6acf11c..daaab3a 100644 (file)
  */
 package jalview.util;
 
-import jalview.javascript.json.JSON;
-
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsEnvironment;
 import java.awt.Toolkit;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
 import java.io.BufferedReader;
 import java.io.File;
@@ -32,14 +34,34 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.reflect.Method;
 import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Properties;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.swing.SwingUtilities;
 
 import org.json.simple.parser.JSONParser;
 import org.json.simple.parser.ParseException;
 
+import com.stevesoft.pat.Regex;
+
+import jalview.bin.Jalview;
+import jalview.javascript.json.JSON;
+import swingjs.api.JSUtilI;
+
 /**
  * System platform information used by Applet and Application
  * 
@@ -54,7 +76,25 @@ public class Platform
   private static Boolean isNoJSMac = null, isNoJSWin = null, isMac = null,
           isWin = null;
 
-  private static Boolean isHeadless = null;
+  private static swingjs.api.JSUtilI jsutil;
+
+  static
+  {
+    if (isJS)
+    {
+      try
+      {
+        // this is ok - it's a highly embedded method in Java; the deprecation
+        // is
+        // really a recommended best practice.
+        jsutil = ((JSUtilI) Class.forName("swingjs.JSUtil").newInstance());
+      } catch (InstantiationException | IllegalAccessException
+              | ClassNotFoundException e)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
 
   /**
    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
@@ -68,6 +108,56 @@ public class Platform
             : isMac);
   }
 
+  public static int SHORTCUT_KEY_MASK = (Platform.isMac()
+          ? KeyEvent.META_DOWN_MASK
+          : KeyEvent.CTRL_DOWN_MASK);
+
+  static
+  {
+    if (!GraphicsEnvironment.isHeadless())
+    {
+      // Using non-deprecated Extended key mask modifiers, but Java 8 has no
+      // getMenuShortcutKeyMaskEx method
+      Toolkit tk = Toolkit.getDefaultToolkit();
+      Method method = null;
+      try
+      {
+        method = tk.getClass().getMethod("getMenuShortcutKeyMaskEx");
+      } catch (Exception e)
+      {
+        System.err.println(
+                "Could not find Toolkit method getMenuShortcutKeyMaskEx. Trying getMenuShortcutKeyMask.");
+      }
+      if (method == null)
+      {
+        try
+        {
+          method = tk.getClass().getMethod("getMenuShortcutKeyMask");
+        } catch (Exception e)
+        {
+          System.err.println(
+                  "Could not find Toolkit method getMenuShortcutKeyMaskEx or getMenuShortcutKeyMask.");
+          e.printStackTrace();
+        }
+      }
+      if (method != null)
+      {
+        try
+        {
+          method.setAccessible(true);
+          SHORTCUT_KEY_MASK = ((int) method.invoke(tk, new Object[0]));
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+        }
+      }
+      if (SHORTCUT_KEY_MASK <= 0xF)
+      {
+        // shift this into the extended region (was Java 8)
+        SHORTCUT_KEY_MASK = SHORTCUT_KEY_MASK << 6;
+      }
+    }
+  }
   /**
    * added to group mouse events into Windows and nonWindows (mac, unix, linux)
    * 
@@ -111,18 +201,6 @@ public class Platform
     return (isNoJSWin == null ? (isNoJSWin = !isJS && isWin()) : isNoJSWin);
   }
 
-  /**
-   * 
-   * @return true if we are running in non-interactive no UI mode
-   */
-  public static boolean isHeadless()
-  {
-    if (isHeadless == null)
-    {
-      isHeadless = "true".equals(System.getProperty("java.awt.headless"));
-    }
-    return isHeadless;
-  }
 
   /**
    * 
@@ -169,30 +247,14 @@ public class Platform
    */
   protected static boolean isControlDown(MouseEvent e, boolean aMac)
   {
-    if (!aMac)
-    {
-      return e.isControlDown();
-
-      // Jalview 2.11 code below: above is as amended for JalviewJS
-      // /*
-      // * answer false for right mouse button
-      // */
-      // if (e.isPopupTrigger())
-      // {
-      // return false;
-      // }
-      // return
-      // (jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() //
-      // .getMenuShortcutKeyMaskEx()
-      // & jalview.util.ShortcutKeyMaskExWrapper
-      // .getModifiersEx(e)) != 0; // getModifiers()) != 0;
-    }
-    // answer false for right mouse button
-    // shortcut key will be META for a Mac
-    return !e.isPopupTrigger()
-            && (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
-                    & e.getModifiers()) != 0;
-    // could we use e.isMetaDown() here?
+    //
+    // System.out.println(e.isPopupTrigger()
+    // + " " + ((SHORTCUT_KEY_MASK & e.getModifiersEx()) != 0)
+    // + " " + e.isControlDown());
+    return (aMac
+            ? !e.isPopupTrigger()
+                    && (SHORTCUT_KEY_MASK & e.getModifiersEx()) != 0
+            : e.isControlDown());
   }
 
   // BH: I don't know about that previous method. Here is what SwingJS uses.
@@ -266,6 +328,20 @@ public class Platform
 
   public static long time, mark, set, duration;
 
+  /**
+   * typical usage:
+   * 
+   * Platform.timeCheck(null, Platform.TIME_MARK);
+   * 
+   * ...
+   * 
+   * Platform.timeCheck("some message", Platform.TIME_MARK);
+   * 
+   * reset...[set/mark]n...get
+   * 
+   * @param msg
+   * @param mode
+   */
   public static void timeCheck(String msg, int mode)
   {
     long t = System.currentTimeMillis();
@@ -273,6 +349,7 @@ public class Platform
     {
     case TIME_RESET:
       time = mark = t;
+      duration = 0;
       if (msg != null)
       {
         System.err.println("Platform: timer reset\t\t\t" + msg);
@@ -281,6 +358,7 @@ public class Platform
     case TIME_MARK:
       if (set > 0)
       {
+        // total time between set/mark points
         duration += (t - set);
       }
       else
@@ -313,84 +391,71 @@ public class Platform
 
   public static void cacheFileData(String path, Object data)
   {
-    if (!isJS() || data == null)
+    if (isJS && data != null)
     {
-      return;
+      jsutil.cachePathData(path, data);
     }
-    /**
-     * @j2sNative
-     * 
-     *            swingjs.JSUtil.cacheFileData$S$O(path, data);
-     * 
-     */
   }
 
   public static void cacheFileData(File file)
   {
-    byte[] data;
-    if (!isJS() || (data = Platform.getFileBytes(file)) == null)
+    if (isJS)
     {
-      return;
+      byte[] data = Platform.getFileBytes(file);
+      {
+        if (data != null)
+        {
+          cacheFileData(file.toString(), data);
+        }
+      }
     }
-    cacheFileData(file.toString(), data);
   }
 
   public static byte[] getFileBytes(File f)
   {
-    return /** @j2sNative f && swingjs.JSUtil.getFileBytes$java_io_File(f) || */
-    null;
+    return (isJS && f != null ? jsutil.getBytes(f) : null);
   }
 
   public static byte[] getFileAsBytes(String fileStr)
   {
-    byte[] bytes = null;
-    // BH 2018 hack for no support for access-origin
-    /**
-     * @j2sNative bytes = swingjs.JSUtil.getFileAsBytes$O(fileStr)
-     */
-    cacheFileData(fileStr, bytes);
-    return bytes;
+    if (isJS && fileStr != null)
+    {
+      byte[] bytes = (byte[]) jsutil.getFile(fileStr, false);
+      cacheFileData(fileStr, bytes);
+      return bytes;
+    }
+    return null;
   }
 
-  @SuppressWarnings("unused")
   public static String getFileAsString(String url)
   {
-    String ret = null;
-    /**
-     * @j2sNative
-     * 
-     *            ret = swingjs.JSUtil.getFileAsString$S(url);
-     * 
-     * 
-     */
-    cacheFileData(url, ret);
-    return ret;
+    if (isJS && url != null)
+    {
+      String ret = (String) jsutil.getFile(url, true);
+      cacheFileData(url, ret);
+      return ret;
+    }
+    return null;
   }
 
   public static boolean setFileBytes(File f, String urlstring)
   {
-    if (!isJS())
+    if (isJS && f != null && urlstring != null)
     {
-      return false;
+      @SuppressWarnings("unused")
+      byte[] bytes = getFileAsBytes(urlstring);
+      jsutil.setFileBytes(f, bytes);
+      return true;
     }
-    @SuppressWarnings("unused")
-    byte[] bytes = getFileAsBytes(urlstring);
-    // TODO temporary doubling of ç§˜bytes and _bytes;
-    // just remove _bytes when new transpiler has been installed
-    /**
-     * @j2sNative f.\u79d8bytes = f._bytes = bytes;
-     */
-    return true;
+    return false;
   }
 
   public static void addJ2SBinaryType(String ext)
   {
-    /**
-     * @j2sNative
-     * 
-     *            J2S._binaryTypes.push("." + ext + "?");
-     * 
-     */
+    if (isJS)
+    {
+      jsutil.addBinaryFileType(ext);
+    }
   }
 
   /**
@@ -413,7 +478,7 @@ public class Platform
    * @param url
    * @return true if window has been opened
    */
-  public static boolean openURL(String url)
+  public static boolean openURL(String url) throws IOException
   {
     if (!isJS())
     {
@@ -430,12 +495,7 @@ public class Platform
 
   public static String getUniqueAppletID()
   {
-    /**
-     * @j2sNative return swingjs.JSUtil.getApplet$()._uniqueId;
-     *
-     */
-    return null;
-
+    return (isJS ? (String) jsutil.getAppletAttribute("_uniqueId") : null);
   }
 
   /**
@@ -448,28 +508,30 @@ public class Platform
    */
   public static void readInfoProperties(String prefix, Properties p)
   {
-    if (!isJS())
+    if (isJS)
     {
-      return;
-    }
-    String id = getUniqueAppletID();
-    String key = "", value = "";
-    /**
-     * @j2sNative var info = swingjs.JSUtil.getApplet$().__Info || {}; for (var
-     *            key in info) { if (key.indexOf(prefix) == 0) { value = "" +
-     *            info[key];
-     */
+      String id = getUniqueAppletID();
 
-    System.out.println(
-            "Platform id=" + id + " reading Info." + key + " = " + value);
-    p.put(id + "_" + key, value);
+      String key = "";
+      String value = "";
+      @SuppressWarnings("unused")
+      Object info = jsutil.getAppletAttribute("__Info");
+      /**
+       * @j2sNative for (key in info) { value = info[key];
+       */
 
-    /**
-     * @j2sNative
-     * 
-     * 
-     *            } }
-     */
+      if (key.indexOf(prefix) == 0)
+      {
+        System.out.println("Platform id=" + id + " reading Info." + key
+                + " = " + value);
+        p.put(key, value);
+
+      }
+
+      /**
+       * @j2sNative }
+       */
+    }
   }
 
   public static void setAjaxJSON(URL url)
@@ -510,8 +572,7 @@ public class Platform
 
   public static Object parseJSON(String json) throws ParseException
   {
-    return (isJS() ? JSON.parse(json)
-            : new JSONParser().parse(json));
+    return (isJS() ? JSON.parse(json) : new JSONParser().parse(json));
   }
 
   public static Object parseJSON(Reader r)
@@ -534,7 +595,6 @@ public class Platform
               "StringJS does not support FileReader parsing for JSON -- but it could...");
     }
     return JSON.parse(r);
-
   }
 
   /**
@@ -543,19 +603,19 @@ public class Platform
    * @param is
    * @param outFile
    * @throws IOException
-   *                       if the file cannot be created or there is a problem
-   *                       reading the input stream.
+   *           if the file cannot be created or there is a problem reading the
+   *           input stream.
    */
   public static void streamToFile(InputStream is, File outFile)
           throws IOException
   {
-    if (isJS() && /**
-                   * @j2sNative outFile.setBytes$O && outFile.setBytes$O(is) &&
-                   */
-            true)
+
+    if (isJS)
     {
+      jsutil.setFileBytes(outFile, is);
       return;
     }
+
     FileOutputStream fio = new FileOutputStream(outFile);
     try
     {
@@ -585,39 +645,46 @@ public class Platform
   public static void addJ2SDirectDatabaseCall(String domain)
   {
 
-    if (isJS())
+    if (isJS)
     {
+      jsutil.addDirectDatabaseCall(domain);
+
       System.out.println(
-            "Platform adding known access-control-allow-origin * for domain "
-                    + domain);
-      /**
-       * @j2sNative
-       * 
-       *            J2S.addDirectDatabaseCall(domain);
-       */
+              "Platform adding known access-control-allow-origin * for domain "
+                      + domain);
     }
 
   }
 
+  /**
+   * Allow for URL-line command arguments. Untested.
+   * 
+   */
   public static void getURLCommandArguments()
   {
 
-    /**
-     * Retrieve the first query field as command arguments to Jalview. Include
-     * only if prior to "?j2s" or "&j2s" or "#". Assign the applet's __Info.args
-     * element to this value.
-     * 
-     * @j2sNative var a =
-     *            decodeURI((document.location.href.replace("&","?").split("?j2s")[0]
-     *            + "?").split("?")[1].split("#")[0]); a &&
-     *            (J2S.thisApplet.__Info.args = a.split(" "));
-     */
-
+    try
+    {
+      /**
+       * Retrieve the first query field as command arguments to Jalview. Include
+       * only if prior to "?j2s" or "&j2s" or "#". Assign the applet's
+       * __Info.args element to this value.
+       * 
+       * @j2sNative var a =
+       *            decodeURI((document.location.href.replace("&","?").split("?j2s")[0]
+       *            + "?").split("?")[1].split("#")[0]); a &&
+       *            (J2S.thisApplet.__Info.args = a.split(" "));
+       * 
+       *            System.out.println("URL arguments: " + a);
+       */
+    } catch (Throwable t)
+    {
+    }
   }
 
   /**
-   * A (case sensitive) file path comparator that ignores the difference between /
-   * and \
+   * A (case sensitive) file path comparator that ignores the difference between
+   * / and \
    * 
    * @param path1
    * @param path2
@@ -637,4 +704,329 @@ public class Platform
     String p2 = path2.replace('\\', '/');
     return p1.equals(p2);
   }
+
+  ///////////// JAL-3253 Applet additions //////////////
+
+  /**
+   * Retrieve the object's embedded size from a div's style on a page if
+   * embedded in SwingJS.
+   * 
+   * @param frame
+   *          JFrame or JInternalFrame
+   * @param defaultWidth
+   *          use -1 to return null (no default size)
+   * @param defaultHeight
+   * @return the embedded dimensions or null (no default size or not embedded)
+   */
+  public static Dimension getDimIfEmbedded(Component frame,
+          int defaultWidth, int defaultHeight)
+  {
+    Dimension d = null;
+    if (isJS)
+    {
+      d = (Dimension) getEmbeddedAttribute(frame, "dim");
+    }
+    return (d == null && defaultWidth >= 0
+            ? new Dimension(defaultWidth, defaultHeight)
+            : d);
+
+  }
+
+  public static Regex newRegex(String regex)
+  {
+    return newRegex(regex, null);
+  }
+
+  public static Regex newRegex(String searchString, String replaceString)
+  {
+    ensureRegex();
+    return (replaceString == null ? new Regex(searchString)
+            : new Regex(searchString, replaceString));
+  }
+
+  public static Regex newRegexPerl(String code)
+  {
+    ensureRegex();
+    return Regex.perlCode(code);
+  }
+
+  /**
+   * Initialize Java debug logging. A representative sample -- adapt as desired.
+   */
+  public static void startJavaLogging()
+  {
+    /**
+     * @j2sIgnore
+     */
+    {
+      logClass("java.awt.EventDispatchThread", "java.awt.EventQueue",
+              "java.awt.Component", "java.awt.focus.Component",
+              "java.awt.event.Component",
+              "java.awt.focus.DefaultKeyboardFocusManager");
+    }
+  }
+
+  /**
+   * Initiate Java logging for a given class. Only for Java, not JavaScript;
+   * Allows debugging of complex event processing.
+   * 
+   * @param className
+   */
+  public static void logClass(String... classNames)
+  {
+    /**
+     * @j2sIgnore
+     * 
+     * 
+     */
+    {
+      Logger rootLogger = Logger.getLogger("");
+      rootLogger.setLevel(Level.ALL);
+      ConsoleHandler consoleHandler = new ConsoleHandler();
+      consoleHandler.setLevel(Level.ALL);
+      for (int i = classNames.length; --i >= 0;)
+      {
+        Logger logger = Logger.getLogger(classNames[i]);
+        logger.setLevel(Level.ALL);
+        logger.addHandler(consoleHandler);
+      }
+    }
+  }
+
+  /**
+   * load a resource -- probably a core file -- if and only if a particular
+   * class has not been instantialized. We use a String here because if we used
+   * a .class object, that reference itself would simply load the class, and we
+   * want the core package to include that as well.
+   * 
+   * @param resourcePath
+   * @param className
+   */
+  public static void loadStaticResource(String resourcePath,
+          String className)
+  {
+    if (isJS)
+    {
+      jsutil.loadResourceIfClassUnknown(resourcePath, className);
+    }
+  }
+
+  public static void ensureRegex()
+  {
+    if (isJS)
+    {
+      loadStaticResource("core/core_stevesoft.z.js",
+              "com.stevesoft.pat.Regex");
+    }
+  }
+
+  /**
+   * Set the "app" property of the HTML5 applet object, for example,
+   * "testApplet.app", to point to the Jalview instance. This will be the object
+   * that page developers use that is similar to the original Java applet object
+   * that was accessed via LiveConnect.
+   * 
+   * @param j
+   */
+  public static void setAppClass(Object j)
+  {
+    if (isJS)
+    {
+      jsutil.setAppClass(j);
+    }
+  }
+
+  /**
+   *
+   * If this frame is embedded in a web page, return a known type.
+   * 
+   * @param frame
+   *          a JFrame or JInternalFrame
+   * @param type
+   *          "name", "node", "init", "dim", or any DOM attribute, such as "id"
+   * @return null if frame is not embedded.
+   */
+  public static Object getEmbeddedAttribute(Component frame, String type)
+  {
+    return (isJS ? jsutil.getEmbeddedAttribute(frame, type) : null);
+  }
+
+  public static void stackTrace()
+  {
+    try
+    {
+      throw new NullPointerException();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+
+  }
+
+  public static URL getDocumentBase()
+  {
+    return (isJS ? jsutil.getDocumentBase() : null);
+  }
+
+  public static URL getCodeBase()
+  {
+    return (isJS ? jsutil.getCodeBase() : null);
+  }
+
+  public static String getUserPath(String subpath)
+  {
+    char sep = File.separatorChar;
+    return System.getProperty("user.home") + sep
+            + subpath.replace('/', sep);
+  }
+
+  /**
+   * This method enables checking if a cached file has exceeded a certain
+   * threshold(in days)
+   * 
+   * @param file
+   *          the cached file
+   * @param noOfDays
+   *          the threshold in days
+   * @return
+   */
+  public static boolean isFileOlderThanThreshold(File file, int noOfDays)
+  {
+    if (isJS())
+    {
+      // not meaningful in SwingJS -- this is a session-specific temp file. It
+      // doesn't have a timestamp.
+      return false;
+    }
+    Path filePath = file.toPath();
+    BasicFileAttributes attr;
+    int diffInDays = 0;
+    try
+    {
+      attr = Files.readAttributes(filePath, BasicFileAttributes.class);
+      diffInDays = (int) ((new Date().getTime()
+              - attr.lastModifiedTime().toMillis())
+              / (1000 * 60 * 60 * 24));
+      // System.out.println("Diff in days : " + diffInDays);
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    return noOfDays <= diffInDays;
+  }
+
+  /**
+   * Get the leading integer part of a string that begins with an integer.
+   * 
+   * @param input
+   *          - the string input to process
+   * @param failValue
+   *          - value returned if unsuccessful
+   * @return
+   */
+  public static int getLeadingIntegerValue(String input, int failValue)
+  {
+    if (input == null)
+    {
+      return failValue;
+    }
+    if (isJS)
+    {
+      int val = /** @j2sNative 1 ? parseInt(input) : */
+              0;
+      return (val == val + 0 ? val : failValue);
+    }
+    // JavaScript does not support Regex ? lookahead
+    String[] parts = input.split("(?=\\D)(?<=\\d)");
+    if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+"))
+    {
+      return Integer.valueOf(parts[0]);
+    }
+    return failValue;
+  }
+
+  public static Map<String, Object> getAppletInfoAsMap()
+  {
+    return (isJS ? jsutil.getAppletInfoAsMap() : null);
+  }
+
+  /**
+   * Get the SwingJS applet ID and combine that with the frameType
+   * 
+   * @param frameType
+   *          "alignment", "desktop", etc., or null
+   * @return
+   */
+  public static String getAppID(String frameType)
+  {
+
+    String id = Jalview.getInstance().j2sAppletID;
+    if (id == null)
+    {
+      Jalview.getInstance().j2sAppletID = id = (isJS
+              ? (String) jsutil.getAppletAttribute("_id")
+              : "jalview");
+    }
+    return id + (frameType == null ? "" : "-" + frameType);
+  }
+
+  /**
+   * Option to avoid unnecessary seeking of nonexistent resources in JavaScript.
+   * Works in Java as well.
+   * 
+   * @param loc
+   * @return
+   */
+  public static Locale getLocaleOrNone(Locale loc)
+  {
+    return (isJS && loc.getLanguage() == "en" ? new Locale("") : loc);
+  }
+
+  /**
+   * From UrlDownloadClient; trivial in JavaScript; painful in Java.
+   * 
+   * @param urlstring
+   * @param outfile
+   * @throws IOException
+   */
+  public static void download(String urlstring, String outfile)
+          throws IOException
+  {
+    Path temp = null;
+    try (InputStream is = new URL(urlstring).openStream())
+    {
+      if (isJS)
+      { // so much easier!
+        streamToFile(is, new File(outfile));
+        return;
+      }
+      temp = Files.createTempFile(".jalview_", ".tmp");
+      try (FileOutputStream fos = new FileOutputStream(temp.toString());
+              ReadableByteChannel rbc = Channels.newChannel(is))
+      {
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        // copy tempfile to outfile once our download completes
+        // incase something goes wrong
+        Files.copy(temp, Paths.get(outfile),
+                StandardCopyOption.REPLACE_EXISTING);
+      }
+    } catch (IOException e)
+    {
+      throw e;
+    } finally
+    {
+      try
+      {
+        if (temp != null)
+        {
+          Files.deleteIfExists(temp);
+        }
+      } catch (IOException e)
+      {
+        System.out.println("Exception while deleting download temp file: "
+                + e.getMessage());
+      }
+    }
+  }
+
 }
index 83330b9..4c33cf8 100644 (file)
  */
 package jalview.util;
 
-import java.awt.event.MouseEvent;
-
 public class ShortcutKeyMaskExWrapper
 {
-
-  private static final Float specversion;
-
-  private static final float modern;
-
-  public static final int SHIFT_DOWN_MASK;
-
-  public static final int ALT_DOWN_MASK;
-
-  private static final ShortcutKeyMaskExWrapperI wrapper;
-
-  static
-  {
-    specversion = Platform.isJS() ? Float.valueOf(8)
-            : Float.parseFloat(
-                    System.getProperty("java.specification.version"));
-    modern = 11;
-
-    if (specversion >= modern)
-    {
-      wrapper = new jalview.util.ShortcutKeyMaskExWrapper11();
-      SHIFT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper11.SHIFT_DOWN_MASK;
-      ALT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper11.ALT_DOWN_MASK;
-    }
-    else
-    {
-      wrapper = new jalview.util.ShortcutKeyMaskExWrapper8();
-      SHIFT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper8.SHIFT_DOWN_MASK;
-      ALT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper8.ALT_DOWN_MASK;
-    }
-  }
-
-  public static int getMenuShortcutKeyMaskEx()
-  {
-    return wrapper.getMenuShortcutKeyMaskEx();
-  }
-
-  public static int getModifiersEx(MouseEvent e)
-  {
-    return wrapper.getModifiersEx(e);
-  }
+//
+//  public static int SHIFT_DOWN_MASK = KeyEvent.SHIFT_DOWN_MASK;
+//
+//  public static int ALT_DOWN_MASK = KeyEvent.ALT_DOWN_MASK;
+//
+//  public static int SHORTCUT_KEY_MASK = (Platform.isMac() ? KeyEvent.META_DOWN_MASK : KeyEvent.CTRL_DOWN_MASK);
+// 
+//  static
+//  {
+//    if (!GraphicsEnvironment.isHeadless())
+//    {
+//      try
+//      {
+//
+//        Class<? extends Toolkit> tk = Toolkit.getDefaultToolkit().getClass();
+//        Method method = tk.getMethod("getMenuShortcutKeyMaskEx");
+//        if (method == null)
+//          method = tk.getMethod("getMenuShortcutKeyMask");
+//        SHORTCUT_KEY_MASK = ((int) method.invoke(tk, new Object[0]));
+//        if (SHORTCUT_KEY_MASK <= 0xF)
+//        {
+//          // shift this into the extended region (was Java 8)
+//          SHORTCUT_KEY_MASK = SHORTCUT_KEY_MASK << 6;
+//        }
+//      } catch (Exception e)
+//      {
+//      }
+//    }
+//  }
+//
+//  public static int getMenuShortcutKeyMaskEx()
+//  {
+//    return SHORTCUT_KEY_MASK;
+//  }
 
 }
index 74c565d..a00e0d3 100644 (file)
@@ -24,9 +24,6 @@ import java.awt.event.MouseEvent;
 
 public interface ShortcutKeyMaskExWrapperI
 {
-  public static int SHIFT_DOWN_MASK = 0;
-
-  public static int ALT_DOWN_MASK = 0;
 
   public int getMenuShortcutKeyMaskEx();
 
index 1f33522..f0bc177 100644 (file)
@@ -545,4 +545,29 @@ public class StringUtils
     }
     return enc;
   }
+
+  /**
+   * Answers true if the string is not empty and consists only of digits, or
+   * characters 'a'-'f' or 'A'-'F', else false
+   * 
+   * @param s
+   * @return
+   */
+  public static boolean isHexString(String s)
+  {
+    int j = s.length();
+    if (j == 0)
+    {
+      return false;
+    }
+    for (int i = 0; i < j; i++)
+    {
+      int c = s.charAt(i);
+      if (!(c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
 }
index 828a72f..58d52da 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.util;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.jar.JarInputStream;
 
@@ -45,4 +46,9 @@ public interface jarInputStreamProvider
    *         with it
    */
   String getFilename();
+
+  /**
+   * @return jarFile name if from SwingJS
+   */
+  File getFile();
 }
index 4311d14..4aec9d2 100644 (file)
@@ -87,6 +87,9 @@ import java.util.Map;
 public abstract class AlignmentViewport
         implements AlignViewportI, CommandListener, VamsasSource
 {
+  public static final String PROPERTY_ALIGNMENT = "alignment";
+  public static final String PROPERTY_SEQUENCE = "sequence";
+
   protected ViewportRanges ranges;
 
   protected ViewStyleI viewStyle = new ViewStyle();
@@ -630,10 +633,31 @@ public abstract class AlignmentViewport
 
   protected ColumnSelection colSel = new ColumnSelection();
 
-  public boolean autoCalculateConsensus = true;
+  protected boolean autoCalculateConsensusAndConservation = true;
+
+  public boolean getAutoCalculateConsensusAndConservation()
+  { // BH 2019.07.24
+    return autoCalculateConsensusAndConservation;
+  }
+
+  public void setAutoCalculateConsensusAndConservation(boolean b)
+  {
+    autoCalculateConsensusAndConservation = b;
+  }
 
   protected boolean autoCalculateStrucConsensus = true;
 
+  public boolean getAutoCalculateStrucConsensus()
+  { // BH 2019.07.24
+    return autoCalculateStrucConsensus;
+  }
+
+  public void setAutoCalculateStrucConsensus(boolean b)
+  {
+    autoCalculateStrucConsensus = b;
+  }
+
+
   protected boolean ignoreGapsInConsensusCalculation = false;
 
   protected ResidueShaderI residueShading = new ResidueShader();
@@ -861,7 +885,7 @@ public abstract class AlignmentViewport
     // see note in mantis : issue number 8585
     if (alignment.isNucleotide()
             || (conservation == null && quality == null)
-            || !autoCalculateConsensus)
+            || !autoCalculateConsensusAndConservation)
     {
       return;
     }
@@ -879,7 +903,7 @@ public abstract class AlignmentViewport
   public void updateConsensus(final AlignmentViewPanel ap)
   {
     // see note in mantis : issue number 8585
-    if (consensus == null || !autoCalculateConsensus)
+    if (consensus == null || !autoCalculateConsensusAndConservation)
     {
       return;
     }
@@ -1389,21 +1413,22 @@ public abstract class AlignmentViewport
    * checks current colsel against record of last hash value, and optionally
    * updates record.
    * 
-   * @param b
+   * @param updateHash
    *          update the record of last hash value
    * @return true if colsel changed since last call (when b is true)
    */
-  public boolean isColSelChanged(boolean b)
+  public boolean isColSelChanged(boolean updateHash)
   {
     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
     if (hc != -1 && hc != colselhash)
     {
-      if (b)
+      if (updateHash)
       {
         colselhash = hc;
       }
       return true;
     }
+    notifySequence();
     return false;
   }
 
@@ -1476,22 +1501,6 @@ public abstract class AlignmentViewport
     }
   }
 
-  /**
-   * Property change listener for changes in alignment
-   * 
-   * @param prop
-   *          DOCUMENT ME!
-   * @param oldvalue
-   *          DOCUMENT ME!
-   * @param newvalue
-   *          DOCUMENT ME!
-   */
-  public void firePropertyChange(String prop, Object oldvalue,
-          Object newvalue)
-  {
-    changeSupport.firePropertyChange(prop, oldvalue, newvalue);
-  }
-
   // common hide/show column stuff
 
   public void hideSelectedColumns()
@@ -1556,13 +1565,14 @@ public abstract class AlignmentViewport
 
       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
 
-      firePropertyChange("alignment", null, alignment.getSequences());
       // used to set hasHiddenRows/hiddenRepSequences here, after the property
       // changed event
+      notifySequence();
       sendSelection();
     }
   }
 
+
   public void showSequence(int index)
   {
     int startSeq = ranges.getStartSeq();
@@ -1585,8 +1595,7 @@ public abstract class AlignmentViewport
       }
 
       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
-
-      firePropertyChange("alignment", null, alignment.getSequences());
+      notifyAlignment();
       sendSelection();
     }
   }
@@ -1620,7 +1629,7 @@ public abstract class AlignmentViewport
         setSequenceAnnotationsVisible(seq[i], false);
       }
       ranges.setStartSeq(startSeq);
-      firePropertyChange("alignment", null, alignment.getSequences());
+      notifyAlignment();
     }
   }
 
@@ -1985,11 +1994,11 @@ public abstract class AlignmentViewport
     {
       alignment.padGaps();
     }
-    if (autoCalculateConsensus)
+    if (autoCalculateConsensusAndConservation)
     {
       updateConsensus(ap);
     }
-    if (hconsensus != null && autoCalculateConsensus)
+    if (hconsensus != null && autoCalculateConsensusAndConservation)
     {
       updateConservation(ap);
     }
@@ -3244,7 +3253,6 @@ public abstract class AlignmentViewport
       codingComplement.setUpdateStructures(needToUpdateStructureViews);
     }
   }
-
   /**
    * Filters out sequences with an eValue higher than the specified value. The
    * filtered sequences are hidden or deleted. Sequences with no eValues are also
@@ -3285,5 +3293,21 @@ public abstract class AlignmentViewport
         hideSequence(new SequenceI[] { seq });
       }
     }
+  }  
+
+  /**
+   * Notify TreePanel and AlignmentPanel of some sort of alignment change.
+   */
+  public void notifyAlignment()
+  {
+    changeSupport.firePropertyChange(PROPERTY_ALIGNMENT, null, alignment.getSequences());
+  }
+  
+  /**
+   * Notify AlignmentPanel of a sequence column selection or visibility changes.
+   */
+  public void notifySequence()
+  {
+    changeSupport.firePropertyChange(PROPERTY_SEQUENCE, null, null);
   }
 }
index 0235081..c153dc1 100644 (file)
@@ -26,6 +26,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
 
+import java.awt.Dimension;
 import java.awt.Graphics;
 
 public abstract class OverviewDimensions
@@ -71,15 +72,22 @@ public abstract class OverviewDimensions
    *          true if the annotation panel is to be shown, false otherwise
    */
   public OverviewDimensions(ViewportRanges ranges,
-          boolean showAnnotationPanel)
+          boolean showAnnotationPanel, Dimension dim)
   {
+    if (!showAnnotationPanel)
+    {
+      graphHeight = 0;
+    }
+
     // scale the initial size of overviewpanel to shape of alignment
     float initialScale = (float) ranges.getAbsoluteAlignmentWidth()
             / (float) ranges.getAbsoluteAlignmentHeight();
 
-    if (!showAnnotationPanel)
+    if (dim != null)
     {
-      graphHeight = 0;
+      width = dim.width;
+      sequencesHeight = dim.height;
+      return;
     }
 
     if (ranges.getAbsoluteAlignmentWidth() > ranges
index de90a21..598b989 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.viewmodel;
 
+import java.awt.Dimension;
+
 import jalview.api.AlignmentColsCollectionI;
 import jalview.api.AlignmentRowsCollectionI;
 import jalview.datamodel.AlignmentI;
@@ -38,10 +40,21 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   private int ydiff; // when dragging, difference in alignment units between
                      // start sequence and original mouse click position
 
+  /**
+   * for testng only
+   * 
+   * @param vpranges
+   * @param showAnnotationPanel
+   */  
+  @Deprecated
+  public OverviewDimensionsHideHidden(ViewportRanges vpranges, boolean showAnnotationPanel) {
+    this(vpranges, showAnnotationPanel, null);
+  }
+
   public OverviewDimensionsHideHidden(ViewportRanges vpranges,
-          boolean showAnnotationPanel)
+          boolean showAnnotationPanel, Dimension dim)
   {
-    super(vpranges, showAnnotationPanel);
+    super(vpranges, showAnnotationPanel, dim);
     ranges = vpranges;
     resetAlignmentDims();
   }
index 3aa6e1b..a4f21f0 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.viewmodel;
 
+import java.awt.Dimension;
+
 import jalview.api.AlignmentColsCollectionI;
 import jalview.api.AlignmentRowsCollectionI;
 import jalview.datamodel.AlignmentI;
@@ -38,6 +40,18 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
   private int ydiff; // when dragging, difference in alignment units between
                      // start sequence and original mouse click position
 
+  
+  /**
+   * for testng only
+   * 
+   * @param vpranges
+   * @param showAnnotationPanel
+   */  
+  @Deprecated
+  public OverviewDimensionsShowHidden(ViewportRanges vpranges, boolean showAnnotationPanel) {
+    this(vpranges, showAnnotationPanel, null);
+  }
+
   /**
    * Create an OverviewDimensions object
    * 
@@ -47,9 +61,9 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
    *          true if the annotation panel is to be shown, false otherwise
    */
   public OverviewDimensionsShowHidden(ViewportRanges vpranges,
-          boolean showAnnotationPanel)
+          boolean showAnnotationPanel, Dimension dim)
   {
-    super(vpranges, showAnnotationPanel);
+    super(vpranges, showAnnotationPanel, dim);
     ranges = vpranges;
     resetAlignmentDims();
   }
index 4f671da..b96ac28 100644 (file)
@@ -112,7 +112,7 @@ public class ViewportRanges extends ViewportProperties
    * Set first residue visible in the viewport, and retain the current width.
    * Fires a property change event.
    * 
-   * @param res
+   * @param res 
    *          residue position
    */
   public void setStartRes(int res)
@@ -136,13 +136,25 @@ public class ViewportRanges extends ViewportProperties
     int[] oldvalues = updateStartEndRes(start, end);
     int oldstartres = oldvalues[0];
     int oldendres = oldvalues[1];
+    
+    if (oldstartres == startRes && oldendres == endRes)
+    {
+      return; // BH 2019.07.27 standard check for no changes
+    }
 
+    // "STARTRES" is a misnomer here -- really "STARTORENDRES"
+    // note that this could be "no change" if the range is just being expanded
     changeSupport.firePropertyChange(STARTRES, oldstartres, startRes);
     if (oldstartres == startRes)
     {
+      // only caught in ViewportRangesTest
+      // No listener cares about this
+      // "ENDRES" is a misnomer here -- really "ENDONLYRES"
+      // BH 2019.07.27 adds end change check
+      // fire only if only the end is changed
       // event won't be fired if start positions are same
       // fire an event for the end positions in case they changed
-      changeSupport.firePropertyChange(ENDRES, oldendres, endRes);
+     changeSupport.firePropertyChange(ENDRES, oldendres, endRes);
     }
   }
 
@@ -224,7 +236,6 @@ public class ViewportRanges extends ViewportProperties
    */
   public void setStartEndSeq(int start, int end)
   {
-    // System.out.println("ViewportRange setStartEndSeq " + start + " " + end);
     int[] oldvalues = updateStartEndSeq(start, end);
     int oldstartseq = oldvalues[0];
     int oldendseq = oldvalues[1];
@@ -250,34 +261,16 @@ public class ViewportRanges extends ViewportProperties
    */
   private int[] updateStartEndSeq(int start, int end)
   {
-    int oldstartseq = this.startSeq;
-    int visibleHeight = getVisibleAlignmentHeight();
-    if (start > visibleHeight - 1)
-    {
-      startSeq = Math.max(visibleHeight - 1, 0);
-    }
-    else if (start < 0)
-    {
-      startSeq = 0;
-    }
-    else
-    {
-      startSeq = start;
-    }
 
+//    if (end == 3 && this.endSeq == 14 || end == 13 && this.endSeq == 3) {
+//      new NullPointerException().printStackTrace(System.out);
+//      System.out.println("ViewportRange updateStartEndSeq " + start + " " + end + " " + Thread.currentThread());
+//    }
+    int oldstartseq = this.startSeq;
     int oldendseq = this.endSeq;
-    if (end >= visibleHeight)
-    {
-      endSeq = Math.max(visibleHeight - 1, 0);
-    }
-    else if (end < 0)
-    {
-      endSeq = 0;
-    }
-    else
-    {
-      endSeq = end;
-    }
+    int max = getVisibleAlignmentHeight() - 1;
+    startSeq = Math.max(0,  Math.min(start, max));
+    endSeq = Math.max(0, Math.min(end, max));
     return new int[] { oldstartseq, oldendseq };
   }
 
@@ -392,21 +385,15 @@ public class ViewportRanges extends ViewportProperties
    */
   public void setViewportStartAndWidth(int start, int w)
   {
-    int vpstart = start;
-    if (vpstart < 0)
-    {
-      vpstart = 0;
-    }
-
-    /*
-     * if not wrapped, don't leave white space at the right margin
-     */
+    int vpstart = Math.max(0, start);
+     
     if (!wrappedMode)
     {
-      if ((w <= getVisibleAlignmentWidth())
-              && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1))
+      // if not wrapped, don't leave white space at the right margin
+      int maxStart = getVisibleAlignmentWidth() - w;
+      if (maxStart >= 0)
       {
-        vpstart = getVisibleAlignmentWidth() - w;
+        vpstart = Math.min(vpstart, maxStart);
       }
 
     }
@@ -425,22 +412,15 @@ public class ViewportRanges extends ViewportProperties
    */
   public void setViewportStartAndHeight(int start, int h)
   {
-    int vpstart = start;
-
-    int visHeight = getVisibleAlignmentHeight();
-    if (vpstart < 0)
-    {
-      vpstart = 0;
-    }
-    else if (h <= visHeight && vpstart + h > visHeight)
-    // viewport height is less than the full alignment and we are running off
-    // the bottom
+    int vpstart = Math.max(0, start);
+    int maxStart = getVisibleAlignmentHeight() - h;
+    if (maxStart > 0)
     {
-      vpstart = visHeight - h;
+      // can't start higher than vertical extent will allow
+      // (viewport height is less than the full alignment
+      // and we are running off the bottom)
+      vpstart = Math.min(vpstart, maxStart);
     }
-    // System.out.println("ViewportRanges setviewportStartAndHeight " + vpstart
-    // + " " + start + " " + h + " " + getVisibleAlignmentHeight());
-
     setStartEndSeq(vpstart, vpstart + h - 1);
   }
 
@@ -797,4 +777,10 @@ public class ViewportRanges extends ViewportProperties
 
     return maxScroll;
   }
+  
+
+  @Override
+  public String toString() {
+     return "[ViewportRange " + startSeq + "-" + endSeq + ", "+ startRes + "-" + endRes + "]";
+  }
 }
index 9f8d4e8..ec80576 100644 (file)
@@ -215,6 +215,10 @@ public class AlignCalcManager implements AlignCalcManagerI
     }
   }
 
+  public int getQueueLength() {
+    return inProgress.size();
+  }
+  
   @Override
   public void registerWorker(AlignCalcWorkerI worker)
   {
index 6b8843d..7daa7b4 100644 (file)
  */
 package jalview.ws;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
 import jalview.analysis.AlignSeq;
 import jalview.api.FeatureSettingsModelI;
 import jalview.bin.Cache;
@@ -36,17 +46,6 @@ import jalview.gui.OOMWarning;
 import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
 import jalview.ws.seqfetcher.DbSourceProxy;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
 import uk.ac.ebi.picr.model.UPEntry;
 import uk.ac.ebi.www.picr.AccessionMappingService.AccessionMapperServiceLocator;
 
@@ -74,8 +73,6 @@ public class DBRefFetcher implements Runnable
 
   CutAndPasteTransfer output = new CutAndPasteTransfer();
 
-  boolean running = false;
-
   /**
    * picr client instance
    */
@@ -225,24 +222,13 @@ public class DBRefFetcher implements Runnable
    */
   public void fetchDBRefs(boolean waitTillFinished)
   {
-    // TODO can we not simply write
-    // if (waitTillFinished) { run(); } else { new Thread(this).start(); }
-
-    Thread thread = new Thread(this);
-    thread.start();
-    running = true;
-
     if (waitTillFinished)
     {
-      while (running)
-      {
-        try
-        {
-          Thread.sleep(500);
-        } catch (Exception ex)
-        {
-        }
-      }
+      run();
+    }
+    else
+    {
+      new Thread(this).start();
     }
   }
 
@@ -295,7 +281,6 @@ public class DBRefFetcher implements Runnable
       throw new Error(MessageManager
               .getString("error.implementation_error_must_init_dbsources"));
     }
-    running = true;
     long startTime = System.currentTimeMillis();
     if (progressWindow != null)
     {
@@ -498,7 +483,6 @@ public class DBRefFetcher implements Runnable
     {
       listener.finished();
     }
-    running = false;
   }
 
   /**
index a480176..5c94faf 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.ext.ensembl.EnsemblGene;
 import jalview.ws.dbsources.EmblCdsSource;
 import jalview.ws.dbsources.EmblSource;
@@ -39,8 +41,39 @@ import java.util.List;
  * This implements the run-time discovery of sequence database clients.
  * 
  */
-public class SequenceFetcher extends ASequenceFetcher
+public class SequenceFetcher extends ASequenceFetcher implements ApplicationSingletonI
 {
+  /*
+   * set a mock fetcher here for testing only - reset to null afterwards
+   */
+  private static SequenceFetcher mockFetcher;
+
+  /**
+   * Set the instance object to use (intended for unit testing with mock
+   * objects).
+   * 
+   * Be sure to reset to null in the tearDown method of any tests!
+   * 
+   * @param sf
+   */
+  public static void setMockFetcher(SequenceFetcher sf)
+  {
+    mockFetcher = sf;
+  }
+  
+  /**
+   * Returns a new SequenceFetcher singleton, or a mock object if one has been
+   * set.
+   * 
+   * @return
+   */
+  public static SequenceFetcher getInstance()
+  {
+    return mockFetcher != null ? mockFetcher
+            : (SequenceFetcher) ApplicationSingletonProvider
+                    .getInstance(SequenceFetcher.class);
+  }
+
   /**
    * Thread safe construction of database proxies TODO: extend to a configurable
    * database plugin mechanism where classes are instantiated by reflection and
diff --git a/src/jalview/ws/SequenceFetcherFactory.java b/src/jalview/ws/SequenceFetcherFactory.java
deleted file mode 100644 (file)
index 9cc4960..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview 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 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview 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 Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.ws;
-
-import jalview.ws.seqfetcher.ASequenceFetcher;
-
-public class SequenceFetcherFactory
-{
-
-  private static SequenceFetcher instance;
-
-  /**
-   * Returns a new SequenceFetcher object, or a mock object if one has been set
-   * 
-   * @return
-   */
-  public static ASequenceFetcher getSequenceFetcher()
-  {
-    return instance == null ? new SequenceFetcher() : instance;
-  }
-
-  /**
-   * Set the instance object to use (intended for unit testing with mock
-   * objects).
-   * 
-   * Be sure to reset to null in the tearDown method of any tests!
-   * 
-   * @param sf
-   */
-  public static void setSequenceFetcher(SequenceFetcher sf)
-  {
-    instance = sf;
-  }
-}
index a73af61..796f6e9 100644 (file)
@@ -22,11 +22,13 @@ package jalview.ws.dbsources;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
+import jalview.util.Platform;
 
 import com.stevesoft.pat.Regex;
 
 public class EmblCdsSource extends EmblXmlSource
 {
+  private Regex ACCESSION_REGEX = null;
 
   public EmblCdsSource()
   {
@@ -42,9 +44,14 @@ public class EmblCdsSource extends EmblXmlSource
   @Override
   public Regex getAccessionValidator()
   {
-    return new Regex("^[A-Z]+[0-9]+");
+    if (ACCESSION_REGEX == null)
+    {
+      ACCESSION_REGEX = Platform.newRegex("^[A-Z]+[0-9]+");
+    }
+    return ACCESSION_REGEX;
   }
 
+
   @Override
   public String getDbSource()
   {
index 6bbe2e1..114f6df 100644 (file)
@@ -22,6 +22,7 @@ package jalview.ws.dbsources;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
+import jalview.util.Platform;
 
 import com.stevesoft.pat.Regex;
 
@@ -32,6 +33,8 @@ import com.stevesoft.pat.Regex;
 public class EmblSource extends EmblXmlSource
 {
 
+  private static Regex ACCESSION_REGEX;
+
   public EmblSource()
   {
     super();
@@ -57,7 +60,11 @@ public class EmblSource extends EmblXmlSource
   @Override
   public Regex getAccessionValidator()
   {
-    return new Regex("^[A-Z]+[0-9]+");
+    if (ACCESSION_REGEX == null)
+    {
+      ACCESSION_REGEX = Platform.newRegex("^[A-Z]+[0-9]+");
+    }
+    return ACCESSION_REGEX;
   }
 
   /*
index 9789f12..5198026 100644 (file)
  */
 package jalview.ws.dbsources;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
 import jalview.analysis.SequenceIdMatcher;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
@@ -43,25 +62,6 @@ import jalview.xml.binding.embl.EntryType.Feature.Qualifier;
 import jalview.xml.binding.embl.ROOT;
 import jalview.xml.binding.embl.XrefType;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.stream.FactoryConfigurationError;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
 public abstract class EmblXmlSource extends EbiFileRetrievedProxy
 {
   /*
index a658089..8951a6e 100644 (file)
@@ -36,6 +36,7 @@ import jalview.io.FormatAdapter;
 import jalview.io.PDBFeatureSettings;
 import jalview.structure.StructureImportSettings;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.ebi.EBIFetchClient;
 
 import java.io.File;
@@ -56,6 +57,8 @@ public class Pdb extends EbiFileRetrievedProxy
 
   private static final int PDB_ID_LENGTH = 4;
 
+  private static Regex ACCESSION_REGEX;
+
   public Pdb()
   {
     super();
@@ -80,7 +83,12 @@ public class Pdb extends EbiFileRetrievedProxy
   @Override
   public Regex getAccessionValidator()
   {
-    return new Regex("([1-9][0-9A-Za-z]{3}):?([ _A-Za-z0-9]?)");
+    if (ACCESSION_REGEX == null)
+    {
+      ACCESSION_REGEX = Platform
+              .newRegex("([1-9][0-9A-Za-z]{3}):?([ _A-Za-z0-9]?)");
+    }
+    return ACCESSION_REGEX;
   }
 
   /*
index 9bd2988..fa6c3c7 100644 (file)
  */
 package jalview.ws.dbsources;
 
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import com.stevesoft.pat.Regex;
+
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -30,6 +47,7 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.util.Platform;
 import jalview.util.StringUtils;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 import jalview.xml.binding.uniprot.DbReferenceType;
@@ -39,23 +57,6 @@ import jalview.xml.binding.uniprot.LocationType;
 import jalview.xml.binding.uniprot.PositionType;
 import jalview.xml.binding.uniprot.PropertyType;
 
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Vector;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.stream.FactoryConfigurationError;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-import com.stevesoft.pat.Regex;
-
 /**
  * This class queries the Uniprot database for sequence data, unmarshals the
  * returned XML, and converts it to Jalview Sequence records (including attached
@@ -70,6 +71,8 @@ public class Uniprot extends DbSourceProxyImpl
 
   private static final String BAR_DELIMITER = "|";
 
+  private static Regex ACCESSION_REGEX;
+
   /**
    * Constructor
    */
@@ -102,7 +105,12 @@ public class Uniprot extends DbSourceProxyImpl
   @Override
   public Regex getAccessionValidator()
   {
-    return new Regex("([A-Z]+[0-9]+[A-Z0-9]+|[A-Z0-9]+_[A-Z0-9]+)");
+    if (ACCESSION_REGEX == null)
+    {
+      ACCESSION_REGEX = Platform
+              .newRegex("([A-Z]+[0-9]+[A-Z0-9]+|[A-Z0-9]+_[A-Z0-9]+)");
+    }
+    return ACCESSION_REGEX;
   }
 
   /*
index 65d01b4..9a6c445 100644 (file)
  */
 package jalview.ws.jws1;
 
-import jalview.bin.Cache;
-import jalview.gui.JvOptionPane;
-import jalview.util.MessageManager;
-
 import java.net.URL;
 import java.util.Hashtable;
 import java.util.StringTokenizer;
@@ -34,9 +30,25 @@ import ext.vamsas.IRegistryServiceLocator;
 import ext.vamsas.RegistryServiceSoapBindingStub;
 import ext.vamsas.ServiceHandle;
 import ext.vamsas.ServiceHandles;
+import jalview.bin.Cache;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+import jalview.gui.JvOptionPane;
+import jalview.util.MessageManager;
 
-public class Discoverer implements Runnable
+public class Discoverer implements Runnable, ApplicationSingletonI
 {
+
+  public static Discoverer getInstance()
+  {
+    return (Discoverer) ApplicationSingletonProvider.getInstance(Discoverer.class);
+  }
+
+  private Discoverer()
+  {
+    // use getInstance()
+  }
+
   ext.vamsas.IRegistry registry; // the root registry service.
 
   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
@@ -111,20 +123,24 @@ public class Discoverer implements Runnable
     return server;
   }
 
-  static private java.net.URL RootServiceURL = null;
+  private java.net.URL RootServiceURL = null;
 
-  static public Vector<URL> ServiceURLList = null;
+  private Vector<URL> ServiceURLList = null;
 
-  static private boolean reallyDiscoverServices = true;
+  public Vector<URL> getServiceURLList() {
+    return ServiceURLList;
+  }
+  
+  private boolean reallyDiscoverServices = true;
 
-  public static java.util.Hashtable<String, Vector<ServiceHandle>> services = null;
+  private java.util.Hashtable<String, Vector<ServiceHandle>> services = null;
   // stored by
   // abstractServiceType
   // string
 
-  public static java.util.Vector<ServiceHandle> serviceList = null;
+  public java.util.Vector<ServiceHandle> serviceList = null;
 
-  static private Vector<URL> getDiscoveryURLS()
+  private Vector<URL> getDiscoveryURLS()
   {
     Vector<URL> urls = new Vector<>();
     String RootServiceURLs = jalview.bin.Cache.getDefault("DISCOVERY_URLS",
@@ -177,10 +193,16 @@ public class Discoverer implements Runnable
    */
   static public void doDiscovery()
   {
+    getInstance().discovery();
+  }
+
+  private void discovery()
+  {
     jalview.bin.Cache.log
             .debug("(Re)-Initialising the discovery URL list.");
     try
     {
+      Discoverer d = getInstance();
       reallyDiscoverServices = jalview.bin.Cache
               .getDefault("DISCOVERY_START", false);
       if (reallyDiscoverServices)
@@ -247,9 +269,9 @@ public class Discoverer implements Runnable
       // JBPNote - should do this a better way!
       if (f.getFaultReason().indexOf("(407)") > -1)
       {
-        if (jalview.gui.Desktop.desktop != null)
+        if (jalview.gui.Desktop.getDesktopPane() != null)
         {
-          JvOptionPane.showMessageDialog(jalview.gui.Desktop.desktop,
+          JvOptionPane.showMessageDialog(jalview.gui.Desktop.getDesktopPane(),
                   MessageManager.getString("label.set_proxy_settings"),
                   MessageManager
                           .getString("label.proxy_authorization_failed"),
@@ -285,7 +307,7 @@ public class Discoverer implements Runnable
    *          Hashtable
    * @return boolean
    */
-  static private boolean buildServiceLists(ServiceHandle[] sh,
+  private boolean buildServiceLists(ServiceHandle[] sh,
           Vector<ServiceHandle> cat,
           Hashtable<String, Vector<ServiceHandle>> sscat)
   {
@@ -388,10 +410,32 @@ public class Discoverer implements Runnable
   /**
    * binding service abstract name to handler class
    */
-  private static Hashtable<String, WS1Client> serviceClientBindings;
+  private Hashtable<String, WS1Client> serviceClientBindings;
 
   public static WS1Client getServiceClient(ServiceHandle sh)
   {
+    return getInstance().getClient(sh);
+  }
+  
+  /**
+   * notes on discovery service 1. need to allow multiple discovery source urls.
+   * 2. user interface to add/control list of urls in preferences notes on
+   * wsclient discovery 1. need a classpath property with list of additional
+   * plugin directories 2. optional config to cite specific bindings between
+   * class name and Abstract service name. 3. precedence for automatic discovery
+   * by using getAbstractName for WSClient - user added plugins override default
+   * plugins ? notes on wsclient gui code for gui attachment now moved to
+   * wsclient implementation. Needs more abstraction but approach seems to work.
+   * is it possible to 'generalise' the data retrieval calls further ? current
+   * methods are very specific (gatherForMSA or gatherForSeqOrMsaSecStrPred),
+   * new methods for conservation (group or alignment), treecalc (aligned
+   * profile), seqannot (sequences selected from dataset, annotation back to
+   * dataset).
+   * 
+   */
+
+  private WS1Client getClient(ServiceHandle sh)
+  {
     if (serviceClientBindings == null)
     {
       // get a list from Config or create below
@@ -414,20 +458,9 @@ public class Discoverer implements Runnable
     }
     return instance;
   }
-  /**
-   * notes on discovery service 1. need to allow multiple discovery source urls.
-   * 2. user interface to add/control list of urls in preferences notes on
-   * wsclient discovery 1. need a classpath property with list of additional
-   * plugin directories 2. optional config to cite specific bindings between
-   * class name and Abstract service name. 3. precedence for automatic discovery
-   * by using getAbstractName for WSClient - user added plugins override default
-   * plugins ? notes on wsclient gui code for gui attachment now moved to
-   * wsclient implementation. Needs more abstraction but approach seems to work.
-   * is it possible to 'generalise' the data retrieval calls further ? current
-   * methods are very specific (gatherForMSA or gatherForSeqOrMsaSecStrPred),
-   * new methods for conservation (group or alignment), treecalc (aligned
-   * profile), seqannot (sequences selected from dataset, annotation back to
-   * dataset).
-   * 
-   */
+
+  public static Hashtable<String, Vector<ServiceHandle>> getServices()
+  {
+    return getInstance().services;
+  }
 }
index 3b7bdb6..1c625fa 100644 (file)
@@ -304,7 +304,7 @@ public class JPredClient extends WS1Client
     WsURL = "http://www.compbio.dundee.ac.uk/JalviewWS/services/jpred";
 
     WebserviceInfo wsInfo = new WebserviceInfo(WebServiceJobTitle,
-            WebServiceReference, true);
+            WebServiceReference, Desktop.FRAME_MAKE_VISIBLE);
 
     return wsInfo;
   }
@@ -323,7 +323,7 @@ public class JPredClient extends WS1Client
 
     } catch (Exception ex)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.secondary_structure_prediction_service_couldnt_be_located",
                       new String[]
index e627699..4c78339 100644 (file)
@@ -76,7 +76,7 @@ public class MsaWSClient extends WS1Client
     alignFrame = _alignFrame;
     if (!sh.getAbstractName().equals("MsaWS"))
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.service_called_is_not_msa_service",
                       new String[]
@@ -89,7 +89,7 @@ public class MsaWSClient extends WS1Client
 
     if ((wsInfo = setWebService(sh)) == null)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), MessageManager
               .formatMessage("label.msa_service_is_unknown", new String[]
               { sh.getName() }),
               MessageManager.getString("label.internal_jalview_error"),
index 49de37e..ba3b168 100644 (file)
@@ -82,7 +82,7 @@ public class SeqSearchWSClient extends WS1Client
     // name to service client name
     if (!sh.getAbstractName().equals(this.getServiceActionKey()))
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.service_called_is_not_seq_search_service",
                       new String[]
@@ -95,7 +95,7 @@ public class SeqSearchWSClient extends WS1Client
 
     if ((wsInfo = setWebService(sh)) == null)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.seq_search_service_is_unknown", new String[]
                       { sh.getName() }),
index 6fa41fc..7bffe43 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.ws.jws1;
 
 import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.WebserviceInfo;
 import jalview.util.MessageManager;
 import jalview.ws.WSClient;
@@ -92,8 +93,8 @@ public abstract class WS1Client extends WSClient
     WebserviceInfo wsInfo = null;
     if (!headless)
     {
-      wsInfo = new WebserviceInfo(WebServiceJobTitle, WebServiceReference,
-              true);
+      wsInfo = new WebserviceInfo(WebServiceJobTitle, WebServiceReference, 
+              Desktop.FRAME_MAKE_VISIBLE);
     }
     return wsInfo;
   }
index 98282fa..bef7e29 100644 (file)
@@ -31,13 +31,13 @@ public class JabaWsParamTest
   public static void main(String[] args)
   {
     jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
-            .getDiscoverer();
+            .getInstance();
     int p = 0;
     if (args.length > 0)
     {
       Vector<String> services = new Vector<>();
       services.addElement(args[p++]);
-      Jws2Discoverer.getDiscoverer().setServiceUrls(services);
+      Jws2Discoverer.getInstance().setServiceUrls(services);
     }
     try
     {
index 7cf6993..d391f8b 100644 (file)
@@ -21,6 +21,8 @@
 package jalview.ws.jws2;
 
 import jalview.bin.Cache;
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.gui.AlignFrame;
 import jalview.util.MessageManager;
 import jalview.ws.ServiceChangeListener;
@@ -58,8 +60,20 @@ import compbio.ws.client.Services;
  * @author JimP
  * 
  */
-public class Jws2Discoverer implements WSDiscovererI, Runnable
+public class Jws2Discoverer implements WSDiscovererI, Runnable, ApplicationSingletonI
 {
+
+  /**
+   * Returns the singleton instance of this class.
+   * 
+   * @return
+   */
+  public static Jws2Discoverer getInstance()
+  {
+    return (Jws2Discoverer) ApplicationSingletonProvider
+            .getInstance(Jws2Discoverer.class);
+  }
+
   public static final String COMPBIO_JABAWS = "http://www.compbio.dundee.ac.uk/jabaws";
 
   /*
@@ -68,20 +82,14 @@ public class Jws2Discoverer implements WSDiscovererI, Runnable
   private final static String JWS2HOSTURLS = "JWS2HOSTURLS";
 
   /*
-   * Singleton instance
-   */
-  private static Jws2Discoverer discoverer;
-
-  /*
    * Override for testing only
    */
-  private static List<String> testUrls = null;
+  private List<String> testUrls = null;
 
   // preferred url has precedence over others
   private String preferredUrl;
   
   private Set<ServiceChangeListener> serviceListeners = new CopyOnWriteArraySet<>();
-
   private Vector<String> invalidServiceUrls = null;
 
   private Vector<String> urlsWithoutServices = null;
@@ -331,27 +339,28 @@ public class Jws2Discoverer implements WSDiscovererI, Runnable
    */
   public static void main(String[] args)
   {
+    Jws2Discoverer instance = getInstance();
     if (args.length > 0)
     {
-      testUrls = new ArrayList<>();
+      instance.testUrls = new ArrayList<>();
       for (String url : args)
       {
-        testUrls.add(url);
+        instance.testUrls.add(url);
       }
     }
-    var discoverer = getDiscoverer();
+    var discoverer = getInstance();
     discoverer.addServiceChangeListener((_discoverer, _services) -> {
       if (discoverer.services != null)
       {
         System.out.println("Changesupport: There are now "
                 + discoverer.services.size() + " services");
         int i = 1;
-        for (ServiceWithParameters instance : discoverer.services)
+        for (ServiceWithParameters s_instance : discoverer.services)
         {
           System.out.println(
-                  "Service " + i++ + " " + instance.getClass()
-                          + "@" + instance.getHostURL() + ": "
-                          + instance.getActionText());
+                  "Service " + i++ + " " + s_instance.getClass()
+                          + "@" + s_instance.getHostURL() + ": "
+                          + s_instance.getActionText());
         }
 
       }
@@ -370,19 +379,6 @@ public class Jws2Discoverer implements WSDiscovererI, Runnable
     }
   }
 
-  /**
-   * Returns the singleton instance of this class.
-   * 
-   * @return
-   */
-  public static Jws2Discoverer getDiscoverer()
-  {
-    if (discoverer == null)
-    {
-      discoverer = new Jws2Discoverer();
-    }
-    return discoverer;
-  }
 
   @Override
   public boolean hasServices()
index 130dd12..e44381c 100644 (file)
@@ -120,7 +120,7 @@ public class MsaWSClient extends Jws2Client implements WSMenuEntryProviderI
                     .getEndpoint() instanceof MultipleSequenceAlignmentI))
     {
       // redundant at mo - but may change
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               MessageManager.formatMessage(
                       "label.service_called_is_not_msa_service",
                       new String[]
@@ -135,7 +135,7 @@ public class MsaWSClient extends Jws2Client implements WSMenuEntryProviderI
             .getEndpoint();
     if ((wsInfo = setWebService(sh, false)) == null)
     {
-      JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), MessageManager
               .formatMessage("label.msa_service_is_unknown", new String[]
               { sh.getName() }),
               MessageManager.getString("label.internal_jalview_error"),
index 53b790d..4c807e1 100644 (file)
@@ -181,7 +181,7 @@ public class SeqAnnotationServiceCalcWorker extends AlignCalcWorker
     } catch (ClassCastException cce)
     {
       annotService = null;
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getInstance(),
               MessageManager.formatMessage(
                       "label.service_called_is_not_an_annotation_service",
                       new String[]
index 16d8220..6939db4 100644 (file)
@@ -264,7 +264,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
           @Override
           public void actionPerformed(ActionEvent arg0)
           {
-            Desktop.instance.showUrl(service.getDocumentationUrl());
+            Desktop.getInstance().showUrl(service.getDocumentationUrl());
           }
         });
         annotservice.setToolTipText(
index f01d872..47fb9c6 100644 (file)
@@ -178,7 +178,7 @@ public class Jws2Instance extends ServiceWithParameters
       try
       {
         paramStore = new JabaParamStore(this,
-                (Desktop.instance != null ? Desktop.getUserParameterStore()
+                (Desktop.getInstance() != null ? Desktop.getUserParameterStore()
                         : null));
       } catch (Exception ex)
       {
index c328670..8eeef4d 100644 (file)
@@ -20,6 +20,9 @@
  */
 package jalview.ws.jws2.jabaws2;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
 import jalview.ws.uimodel.AlignAnalysisUIText;
 
 import java.util.HashMap;
@@ -27,11 +30,23 @@ import java.util.HashSet;
 
 import compbio.data.msa.JABAService;
 
-public class Jws2InstanceFactory
+public class Jws2InstanceFactory implements ApplicationSingletonI
 {
-  private static HashMap<String, AlignAnalysisUIText> aaConGUI;
 
-  private static HashSet<String> ignoreGUI;
+  private Jws2InstanceFactory()
+  {
+    // private singleton
+  }
+
+  private static Jws2InstanceFactory getInstance()
+  {
+    return (Jws2InstanceFactory) ApplicationSingletonProvider
+            .getInstance(Jws2InstanceFactory.class);
+  }
+
+  private HashMap<String, AlignAnalysisUIText> aaConGUI;
+
+  private HashSet<String> ignoreGUI;
 
   private static String category_rewrite(String cat_name)
   {
@@ -40,7 +55,7 @@ public class Jws2InstanceFactory
             : cat_name;
   }
 
-  private static void init()
+  private void init()
   {
     if (aaConGUI == null)
     {
@@ -63,8 +78,8 @@ public class Jws2InstanceFactory
    */
   public static boolean ignoreService(String serviceType)
   {
-    init();
-    return (ignoreGUI.contains(serviceType.toString()));
+    getInstance().init();
+    return (getInstance().ignoreGUI.contains(serviceType.toString()));
   }
 
   /**
@@ -82,10 +97,10 @@ public class Jws2InstanceFactory
           String serviceType, String name, String description,
           JABAService service)
   {
-    init();
+    getInstance().init();
     Jws2Instance svc = new Jws2Instance(jwsservers, serviceType,
             category_rewrite(name), description, service);
-    svc.setAlignAnalysisUI(aaConGUI.get(serviceType.toString()));
+    svc.setAlignAnalysisUI(getInstance().aaConGUI.get(serviceType.toString()));
     return svc;
   }
 
index d84206f..2d39db5 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.rest;
 
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentView;
 import jalview.gui.AlignFrame;
@@ -48,8 +50,27 @@ import javax.swing.event.MenuListener;
  * 
  */
 public class RestClient extends WSClient
-        implements WSClientI, WSMenuEntryProviderI
+implements WSClientI, WSMenuEntryProviderI, ApplicationSingletonI
 {
+
+  @SuppressWarnings("unused")
+  private RestClient()
+  {
+    // accessed by ApplicationSingletonProvider
+  }
+
+  
+private static RestClient getInstance()
+{
+return (RestClient) ApplicationSingletonProvider.getInstance(RestClient.class);
+}
+
+public static final String RSBS_SERVICES = "RSBS_SERVICES";
+
+
+  protected Vector<String> services = null;
+
+
   RestServiceDescription service;
 
   public RestClient(RestServiceDescription rsd)
@@ -108,7 +129,7 @@ public class RestClient extends WSClient
     if (!headless)
     {
       wsInfo = new WebserviceInfo(WebServiceJobTitle,
-              WebServiceName + "\n" + WebServiceReference, true);
+              WebServiceName + "\n" + WebServiceReference, Desktop.FRAME_MAKE_VISIBLE);
       wsInfo.setRenderAsHtml(true);
     }
 
@@ -328,7 +349,7 @@ public class RestClient extends WSClient
     else
     {
       // TODO: try to tell the user why the job couldn't be started.
-      JvOptionPane.showMessageDialog(Desktop.desktop,
+      JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
               (jobsthread.hasWarnings() ? jobsthread.getWarnings()
                       : MessageManager.getString(
                               "label.job_couldnt_be_started_check_input")),
@@ -358,12 +379,13 @@ public class RestClient extends WSClient
     return true;
   }
 
-  protected static Vector<String> services = null;
-
-  public static final String RSBS_SERVICES = "RSBS_SERVICES";
-
   public static RestClient[] getRestClients()
   {
+    return getInstance().getClients();
+  }
+    
+  private RestClient[] getClients()
+  {
     if (services == null)
     {
       services = new Vector<>();
@@ -417,10 +439,11 @@ public class RestClient extends WSClient
   {
     if (rsbsUrls != null)
     {
+      
       // TODO: consider validating services ?
-      services = new Vector<>(rsbsUrls);
+      getInstance().services = new Vector<String>(rsbsUrls);
       StringBuffer sprop = new StringBuffer();
-      for (String s : services)
+      for (String s : getInstance().services)
       {
         sprop.append(s);
       }
index d065666..3ec6320 100644 (file)
  */
 package jalview.ws.sifts;
 
-import jalview.analysis.AlignSeq;
-import jalview.analysis.scoremodels.ScoreMatrix;
-import jalview.analysis.scoremodels.ScoreModels;
-import jalview.api.DBRefEntryI;
-import jalview.api.SiftsClientI;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.SequenceI;
-import jalview.io.StructureFile;
-import jalview.schemes.ResidueProperties;
-import jalview.structure.StructureMapping;
-import jalview.util.Comparison;
-import jalview.util.DBRefUtils;
-import jalview.util.Format;
-import jalview.xml.binding.sifts.Entry;
-import jalview.xml.binding.sifts.Entry.Entity;
-import jalview.xml.binding.sifts.Entry.Entity.Segment;
-import jalview.xml.binding.sifts.Entry.Entity.Segment.ListMapRegion.MapRegion;
-import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue;
-import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.CrossRefDb;
-import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.ResidueDetail;
-
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.net.URL;
 import java.net.URLConnection;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -67,10 +40,33 @@ import java.util.TreeMap;
 import java.util.zip.GZIPInputStream;
 
 import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
 import javax.xml.bind.Unmarshaller;
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamReader;
 
+import jalview.analysis.AlignSeq;
+import jalview.analysis.scoremodels.ScoreMatrix;
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.api.DBRefEntryI;
+import jalview.api.SiftsClientI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.SequenceI;
+import jalview.io.StructureFile;
+import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureMapping;
+import jalview.util.Comparison;
+import jalview.util.DBRefUtils;
+import jalview.util.Format;
+import jalview.util.Platform;
+import jalview.xml.binding.sifts.Entry;
+import jalview.xml.binding.sifts.Entry.Entity;
+import jalview.xml.binding.sifts.Entry.Entity.Segment;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListMapRegion.MapRegion;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.CrossRefDb;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.ResidueDetail;
 import mc_view.Atom;
 import mc_view.PDBChain;
 
@@ -100,8 +96,6 @@ public class SiftsClient implements SiftsClientI
    */
   private jalview.datamodel.Mapping seqFromPdbMapping;
 
-  private static final int BUFFER_SIZE = 4096;
-
   public static final int UNASSIGNED = Integer.MIN_VALUE;
 
   private static final int PDB_RES_POS = 0;
@@ -116,10 +110,15 @@ public class SiftsClient implements SiftsClientI
 
   private final static String NEWLINE = System.lineSeparator();
 
+  private static final boolean GET_STREAM = false;
+  private static final boolean CACHE_FILE = true;
+
   private String curSourceDBRef;
 
   private HashSet<String> curDBRefAccessionIdsString;
 
+  private boolean doCache = false;
+
   private enum CoordinateSys
   {
     UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe");
@@ -164,8 +163,31 @@ public class SiftsClient implements SiftsClientI
   {
     this.pdb = pdb;
     this.pdbId = pdb.getId();
-    File siftsFile = getSiftsFile(pdbId);
-    siftsEntry = parseSIFTs(siftsFile);
+    if (doCache) {
+      File siftsFile = getSiftsFile(pdbId);
+      siftsEntry = parseSIFTs(siftsFile);
+    } else {
+      siftsEntry = parseSIFTSStreamFor(pdbId);
+    }
+  }
+
+  /**
+   * A more streamlined version of SIFT reading that allows for streaming of the data.
+   * 
+   * @param pdbId
+   * @return
+   * @throws SiftsException
+   */
+  private static Entry parseSIFTSStreamFor(String pdbId) throws SiftsException
+  {
+    try
+    {
+      InputStream is = (InputStream) downloadSifts(pdbId, GET_STREAM);
+      return parseSIFTs(is);
+    } catch (Exception e)
+    {
+      throw new SiftsException(e.getMessage());
+    }
   }
 
   /**
@@ -179,19 +201,25 @@ public class SiftsClient implements SiftsClientI
    */
   private Entry parseSIFTs(File siftFile) throws SiftsException
   {
-    try (InputStream in = new FileInputStream(siftFile);
-            GZIPInputStream gzis = new GZIPInputStream(in);)
+    try (InputStream in = new FileInputStream(siftFile)) {
+      return parseSIFTs(in);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      throw new SiftsException(e.getMessage());
+    }
+  }
+  
+  private static Entry parseSIFTs(InputStream in) throws Exception {
+    try (GZIPInputStream gzis = new GZIPInputStream(in);)
     {
       // System.out.println("File : " + siftFile.getAbsolutePath());
       JAXBContext jc = JAXBContext.newInstance("jalview.xml.binding.sifts");
       XMLStreamReader streamReader = XMLInputFactory.newInstance()
               .createXMLStreamReader(gzis);
       Unmarshaller um = jc.createUnmarshaller();
-      return (Entry) um.unmarshal(streamReader);
-    } catch (Exception e)
-    {
-      e.printStackTrace();
-      throw new SiftsException(e.getMessage());
+      JAXBElement<Entry> jbe = um.unmarshal(streamReader, Entry.class);
+      return jbe.getValue();
     }
   }
 
@@ -221,14 +249,14 @@ public class SiftsClient implements SiftsClientI
       // The line below is required for unit testing... don't comment it out!!!
       System.out.println(">>> SIFTS File already downloaded for " + pdbId);
 
-      if (isFileOlderThanThreshold(siftsFile,
+      if (Platform.isFileOlderThanThreshold(siftsFile,
               SiftsSettings.getCacheThresholdInDays()))
       {
         File oldSiftsFile = new File(siftsFileName + "_old");
         siftsFile.renameTo(oldSiftsFile);
         try
         {
-          siftsFile = downloadSiftsFile(pdbId.toLowerCase());
+          siftsFile = downloadSiftsFile(pdbId);
           oldSiftsFile.delete();
           return siftsFile;
         } catch (IOException e)
@@ -245,7 +273,7 @@ public class SiftsClient implements SiftsClientI
     }
     try
     {
-      siftsFile = downloadSiftsFile(pdbId.toLowerCase());
+      siftsFile = downloadSiftsFile(pdbId);
     } catch (IOException e)
     {
       throw new SiftsException(e.getMessage());
@@ -254,35 +282,6 @@ public class SiftsClient implements SiftsClientI
   }
 
   /**
-   * This method enables checking if a cached file has exceeded a certain
-   * threshold(in days)
-   * 
-   * @param file
-   *          the cached file
-   * @param noOfDays
-   *          the threshold in days
-   * @return
-   */
-  public static boolean isFileOlderThanThreshold(File file, int noOfDays)
-  {
-    Path filePath = file.toPath();
-    BasicFileAttributes attr;
-    int diffInDays = 0;
-    try
-    {
-      attr = Files.readAttributes(filePath, BasicFileAttributes.class);
-      diffInDays = (int) ((new Date().getTime()
-              - attr.lastModifiedTime().toMillis())
-              / (1000 * 60 * 60 * 24));
-      // System.out.println("Diff in days : " + diffInDays);
-    } catch (IOException e)
-    {
-      e.printStackTrace();
-    }
-    return noOfDays <= diffInDays;
-  }
-
-  /**
    * Download a SIFTs XML file for a given PDB Id from an FTP repository
    * 
    * @param pdbId
@@ -292,39 +291,48 @@ public class SiftsClient implements SiftsClientI
    */
   public static File downloadSiftsFile(String pdbId)
           throws SiftsException, IOException
+  {    
+    return (File) downloadSifts(pdbId, CACHE_FILE);
+  }
+
+  /**
+   * Download SIFTs XML with the option to cache a file or to get a stream.
+   * 
+   * @param pdbId
+   * @param asFile 
+   * @return
+   * @throws IOException
+   */
+  private static Object downloadSifts(String pdbId, boolean asFile) throws IOException
   {
+    pdbId = pdbId.toLowerCase();
     if (pdbId.contains(".cif"))
     {
       pdbId = pdbId.replace(".cif", "");
     }
     String siftFile = pdbId + ".xml.gz";
-    String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile;
-    String downloadedSiftsFile = SiftsSettings.getSiftDownloadDirectory()
-            + siftFile;
-    File siftsDownloadDir = new File(
-            SiftsSettings.getSiftDownloadDirectory());
-    if (!siftsDownloadDir.exists())
+
+    File downloadTo = null;
+    if (asFile)
     {
-      siftsDownloadDir.mkdirs();
+      downloadTo = new File(
+              SiftsSettings.getSiftDownloadDirectory() + siftFile);
+      File siftsDownloadDir = new File(SiftsSettings.getSiftDownloadDirectory());
+      if (!siftsDownloadDir.exists())
+      {
+        siftsDownloadDir.mkdirs();
+      }
     }
-    // System.out.println(">> Download ftp url : " + siftsFileFTPURL);
-    // long now = System.currentTimeMillis();
+    String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile;
     URL url = new URL(siftsFileFTPURL);
     URLConnection conn = url.openConnection();
-    InputStream inputStream = conn.getInputStream();
-    FileOutputStream outputStream = new FileOutputStream(
-            downloadedSiftsFile);
-    byte[] buffer = new byte[BUFFER_SIZE];
-    int bytesRead = -1;
-    while ((bytesRead = inputStream.read(buffer)) != -1)
-    {
-      outputStream.write(buffer, 0, bytesRead);
-    }
-    outputStream.close();
-    inputStream.close();
-    // System.out.println(">>> File downloaded : " + downloadedSiftsFile
-    // + " took " + (System.currentTimeMillis() - now) + "ms");
-    return new File(downloadedSiftsFile);
+    InputStream is = conn.getInputStream();
+    if (!asFile)
+      return is;
+    // This is MUCH more efficent in JavaScript, as we already have the bytes
+    Platform.streamToFile(is, downloadTo);
+    is.close();
+    return downloadTo;
   }
 
   /**
@@ -631,7 +639,7 @@ public class SiftsClient implements SiftsClientI
       for (Residue residue : residues)
       {
         boolean isObserved = isResidueObserved(residue);
-        int pdbeIndex = getLeadingIntegerValue(residue.getDbResNum(),
+        int pdbeIndex = Platform.getLeadingIntegerValue(residue.getDbResNum(),
                 UNASSIGNED);
         int currSeqIndex = UNASSIGNED;
         List<CrossRefDb> cRefDbs = residue.getCrossRefDb();
@@ -643,7 +651,7 @@ public class SiftsClient implements SiftsClientI
             pdbRefDb = cRefDb;
             if (firstPDBResNum == UNASSIGNED)
             {
-              firstPDBResNum = getLeadingIntegerValue(cRefDb.getDbResNum(),
+              firstPDBResNum = Platform.getLeadingIntegerValue(cRefDb.getDbResNum(),
                       UNASSIGNED);
             }
             else
@@ -658,7 +666,7 @@ public class SiftsClient implements SiftsClientI
           if (cRefDb.getDbCoordSys().equalsIgnoreCase(seqCoordSys.getName())
                   && isAccessionMatched(cRefDb.getDbAccessionId()))
           {
-            currSeqIndex = getLeadingIntegerValue(cRefDb.getDbResNum(),
+            currSeqIndex = Platform.getLeadingIntegerValue(cRefDb.getDbResNum(),
                     UNASSIGNED);
             if (pdbRefDb != null)
             {
@@ -708,9 +716,9 @@ public class SiftsClient implements SiftsClientI
         {
 
           int resNum = (pdbRefDb == null)
-                  ? getLeadingIntegerValue(residue.getDbResNum(),
+                  ? Platform.getLeadingIntegerValue(residue.getDbResNum(),
                           UNASSIGNED)
-                  : getLeadingIntegerValue(pdbRefDb.getDbResNum(),
+                  : Platform.getLeadingIntegerValue(pdbRefDb.getDbResNum(),
                           UNASSIGNED);
 
           if (isObserved)
@@ -731,29 +739,6 @@ public class SiftsClient implements SiftsClientI
   }
 
   /**
-   * Get the leading integer part of a string that begins with an integer.
-   * 
-   * @param input
-   *          - the string input to process
-   * @param failValue
-   *          - value returned if unsuccessful
-   * @return
-   */
-  static int getLeadingIntegerValue(String input, int failValue)
-  {
-    if (input == null)
-    {
-      return failValue;
-    }
-    String[] parts = input.split("(?=\\D)(?<=\\d)");
-    if (parts != null && parts.length > 0 && parts[0].matches("[0-9]+"))
-    {
-      return Integer.valueOf(parts[0]);
-    }
-    return failValue;
-  }
-
-  /**
    * 
    * @param chainId
    *          Target chain to populate mapping of its atom positions.
index 5e2c526..1c25a34 100644 (file)
@@ -22,57 +22,76 @@ package jalview.ws.sifts;
 
 import java.util.Objects;
 
-public class SiftsSettings
+import jalview.bin.ApplicationSingletonProvider;
+import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
+
+public class SiftsSettings implements ApplicationSingletonI
 {
-  private static boolean mapWithSifts = false;
+  /**
+   * Constructor
+   * 
+   * @return
+   */
+  private static SiftsSettings getInstance()
+  {
+    return (SiftsSettings) ApplicationSingletonProvider
+            .getInstance(SiftsSettings.class);
+  }
+
+  private SiftsSettings()
+  {
+    // singleton; use getInstance()
+  }
+
+  private boolean mapWithSifts = false;
 
-  private static String siftDownloadDirectory;
+  private String siftDownloadDirectory;
 
-  private static int cacheThresholdInDays;
+  private int cacheThresholdInDays;
 
-  private static int failSafePIDThreshold;
+  private int failSafePIDThreshold;
 
   public static boolean isMapWithSifts()
   {
-    return mapWithSifts;
+    return getInstance().mapWithSifts;
   }
 
   public static void setMapWithSifts(boolean mapWithSifts)
   {
-    SiftsSettings.mapWithSifts = mapWithSifts;
+    getInstance().mapWithSifts = mapWithSifts;
   }
 
   public static String getSiftDownloadDirectory()
   {
-    return siftDownloadDirectory;
+    return getInstance().siftDownloadDirectory;
   }
 
   public static void setSiftDownloadDirectory(String siftDownloadDirectory)
   {
-    SiftsSettings.siftDownloadDirectory = siftDownloadDirectory;
+    getInstance().siftDownloadDirectory = siftDownloadDirectory;
   }
 
   public static int getCacheThresholdInDays()
   {
-    return cacheThresholdInDays;
+    return getInstance().cacheThresholdInDays;
   }
 
   public static void setCacheThresholdInDays(String cacheThresholdInDays)
   {
     Objects.requireNonNull(cacheThresholdInDays);
-    SiftsSettings.cacheThresholdInDays = Integer
+    getInstance().cacheThresholdInDays = Integer
             .valueOf(cacheThresholdInDays);
   }
 
   public static int getFailSafePIDThreshold()
   {
-    return failSafePIDThreshold;
+    return getInstance().failSafePIDThreshold;
   }
 
   public static void setFailSafePIDThreshold(String failSafePIDThreshold)
   {
     Objects.requireNonNull(failSafePIDThreshold);
-    SiftsSettings.failSafePIDThreshold = Integer
+    getInstance().failSafePIDThreshold = Integer
             .valueOf(failSafePIDThreshold);
   }
 }
index 58632f2..7d45313 100644 (file)
 
 package jalview.ws.utils;
 
-import jalview.util.Platform;
-
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.net.URL;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
+
+import jalview.util.Platform;
 
 public class UrlDownloadClient
 {
@@ -53,64 +45,7 @@ public class UrlDownloadClient
   public static void download(String urlstring, String outfile)
           throws IOException
   {
-
-      FileOutputStream fos = null;
-      ReadableByteChannel rbc = null;
-      Path temp = null;
-      try
-      {
-        temp = Files.createTempFile(".jalview_", ".tmp");
-
-        URL url = new URL(urlstring);
-        rbc = Channels.newChannel(url.openStream());
-        fos = new FileOutputStream(temp.toString());
-        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
-
-        // copy tempfile to outfile once our download completes
-        // incase something goes wrong
-        Files.copy(temp, Paths.get(outfile),
-                StandardCopyOption.REPLACE_EXISTING);
-      } catch (IOException e)
-      {
-        throw e;
-      } finally
-      {
-        try
-        {
-          if (fos != null)
-          {
-            fos.close();
-          }
-        } catch (IOException e)
-        {
-          System.out.println(
-                  "Exception while closing download file output stream: "
-                          + e.getMessage());
-        }
-        try
-        {
-          if (rbc != null)
-          {
-            rbc.close();
-          }
-        } catch (IOException e)
-        {
-          System.out.println("Exception while closing download channel: "
-                  + e.getMessage());
-        }
-        try
-        {
-          if (temp != null)
-          {
-            Files.deleteIfExists(temp);
-          }
-        } catch (IOException e)
-        {
-          System.out.println("Exception while deleting download temp file: "
-                  + e.getMessage());
-        }
-      }
-
+      Platform.download(urlstring, outfile);
   }
 
   public static void download(String urlstring, File tempFile) throws IOException
diff --git a/src/javajs/async/Assets.java b/src/javajs/async/Assets.java
new file mode 100644 (file)
index 0000000..05614ce
--- /dev/null
@@ -0,0 +1,594 @@
+package javajs.async;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import swingjs.api.JSUtilI;
+
+/**
+ * The Assets class allows assets such as images and property files to be
+ * combined into zip files rather than delivered individually. The Assets
+ * instance is a singleton served by a set of static methods. In particular, the
+ * three add(...) methods are used to create asset references, which include an
+ * arbitrary name, a path to a zip file asset, and one or more class paths that
+ * are covered by this zip file asset.
+ * 
+ * For example:
+ * 
+ * <code>
+       static {
+               try {
+                       Assets.add(new Assets.Asset("osp", "osp-assets.zip", "org/opensourcephysics/resources"));
+                       Assets.add(new Assets.Asset("tracker", "tracker-assets.zip",
+                                       "org/opensourcephysics/cabrillo/tracker/resources"));
+                       Assets.add(new Assets.Asset("physlets", "physlet-assets.zip", new String[] { "opticsimages", "images" }));
+                       // add the Info.assets last so that it can override these defaults
+                       if (OSPRuntime.isJS) {
+                               Assets.add(OSPRuntime.jsutil.getAppletInfo("assets"));
+                       }
+               } catch (Exception e) {
+                       OSPLog.warning("Error reading assets path. ");
+               }
+
+       }
+ * </code>
+ * 
+ * It is not clear that Java is well-served by this zip-file loading, but
+ * certainly JavaScript is. What could be 100 downloads is just one, and SwingJS
+ * (but not Java) can cache individual ZipEntry instances in order to unzip them
+ * independently only when needed. This is potentially a huge savings.
+ * 
+ * Several static methods can be used to retrieve assets. Principal among those
+ * are:
+ * 
+ * <code>
+ * getAssetBytes(String fullPath)
+ * getAssetString(String fullPath)
+ * getAssetStream(String fullPath)
+ * </code>
+ * 
+ * If an asset is not found in a zip file, then it will be loaded from its fullPath. 
+ * 
+ * 
+ * 
+ * @author hansonr
+ *
+ */
+
+public class Assets {
+
+       public static boolean isJS = /** @j2sNative true || */
+                       false;
+
+       public static JSUtilI jsutil;
+
+       static {
+               try {
+                       if (isJS) {
+                               jsutil = ((JSUtilI) Class.forName("swingjs.JSUtil").newInstance());
+                       }
+
+               } catch (Exception e) {
+                       System.err.println("Assets could not create swinjs.JSUtil instance");
+               }
+       }
+
+       private Map<String, Map<String, ZipEntry>> htZipContents = new HashMap<>();
+
+       private static boolean doCacheZipContents = true;
+
+       private static Assets instance = new Assets();
+
+       private Assets() {
+       }
+
+       private Map<String, Asset> assetsByPath = new HashMap<>();
+
+       private String[] sortedList = new String[0];
+
+       /**
+        * If this object has been cached by SwingJS, add its bytes to the URL, URI, or
+        * File
+        * 
+        * @param URLorURIorFile
+        * @return
+        */
+       public static byte[] addJSCachedBytes(Object URLorURIorFile) {
+               return (isJS ? jsutil.addJSCachedBytes(URLorURIorFile) : null);
+       }
+
+       public static class Asset {
+               String name;
+               URI uri;
+               String classPath;
+               String zipPath;
+               String[] classPaths;
+
+               public Asset(String name, String zipPath, String[] classPaths) {
+                       this.name = name;
+                       this.zipPath = zipPath;
+                       this.classPaths = classPaths;
+               }
+
+               public Asset(String name, String zipPath, String classPath) {
+                       this.name = name;
+                       this.zipPath = zipPath;
+                       uri = getAbsoluteURI(zipPath); // no spaces expected here.
+                       this.classPath = classPath.endsWith("/") ? classPath : classPath + "/";
+               }
+
+               public URL getURL(String fullPath) throws MalformedURLException {
+                       return (fullPath.indexOf(classPath) < 0 ? null
+                                       : new URL("jar", null, uri + "!/" + fullPath));//.replaceAll(" ", "%20")));
+               }
+
+               @Override
+               public String toString() {
+                       return "{" + "\"name\":" + "\"" + name + "\"," + "\"zipPath\":" + "\"" + zipPath + "\"," + "\"classPath\":"
+                                       + "\"" + classPath + "\"" + "}";
+               }
+
+       }
+
+       public static Assets getInstance() {
+               return instance;
+       }
+
+       /**
+        * The difference here is that URL will not insert the %20 for space that URI will.
+        * 
+        * @param path
+        * @return
+        */
+       @SuppressWarnings("deprecation")
+       public static URL getAbsoluteURL(String path) {
+               URL url = null;
+               try {
+                       url = (path.indexOf(":/") < 0 ? new File(new File(path).getAbsolutePath()).toURL() : new URL(path));
+                       if (path.indexOf("!/")>=0)
+                               url = new URL("jar", null, url.toString());
+               } catch (MalformedURLException e) {
+                       e.printStackTrace();
+               }
+               return url;
+       }
+
+       public static URI getAbsoluteURI(String path) {
+               URI uri = null;
+               try {
+                       uri = (path.indexOf(":/") < 0 ? new File(new File(path).getAbsolutePath()).toURI() : new URI(path));
+               } catch (URISyntaxException e) {
+                       e.printStackTrace();
+               }
+               return uri;
+       }
+
+       /**
+        * Allows passing a Java Asset or array of Assets or a JavaScript Object or
+        * Object array that contains name, zipPath, and classPath keys; in JavaScript,
+        * the keys can have multiple .
+        * 
+        * @param o
+        */
+       public static void add(Object o) {
+               if (o == null)
+                       return;
+               try {
+                       if (o instanceof Object[]) {
+                               Object[] a = (Object[]) o;
+                               for (int i = 0; i < a.length; i++)
+                                       add(a[i]);
+                               return;
+                       }
+                       // In JavaScript this may not actually be an Asset, only a proxy for that.
+                       // Just testing for keys. Only one of classPath and classPaths is allowed.
+                       Asset a = (Asset) o;
+                       if (a.name == null || a.zipPath == null || a.classPath == null && a.classPaths == null
+                                       || a.classPath != null && a.classPaths != null) {
+                               throw new NullPointerException("Assets could not parse " + o);
+                       }
+                       if (a.classPaths == null) {
+                               // not possible in Java, but JavaScript may be passing an array of class paths
+                               add(a.name, a.zipPath, a.classPath);
+                       } else {
+                               add(a.name, a.zipPath, a.classPaths);
+                       }
+               } catch (Throwable t) {
+                       throw new IllegalArgumentException(t.getMessage());
+               }
+       }
+
+       public static void add(String name, String zipFile, String path) {
+               add(name, zipFile, new String[] { path });
+       }
+
+       private static HashSet<String> loadedAssets = new HashSet<>();
+       
+       public static boolean hasLoaded(String name) {
+               return loadedAssets.contains(name);
+       }
+       
+       public static void reset() {
+               getInstance().htZipContents.clear();
+               getInstance().assetsByPath.clear();
+               getInstance().sortedList = new String[0];
+       }
+
+       public static void add(String name, String zipFile, String[] paths) {
+               getInstance()._add(name, zipFile, paths);
+       }
+
+       private void _add(String name, String zipFile, String[] paths) {
+               if (hasLoaded(name)) {
+                       System.err.println("Assets warning: Asset " + name + " already exists");
+               }
+               loadedAssets.add(name);
+               for (int i = paths.length; --i >= 0;) {
+                       assetsByPath.put(paths[i], new Asset(name, zipFile, paths[i]));
+               }
+               resort();
+       }
+       
+
+       /**
+        * Gets the asset, preferably from a zip file asset, but not necessarily.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static byte[] getAssetBytes(String assetPath) {
+               return getAssetBytes(assetPath, false);
+       }
+
+       /**
+        * Gets the asset, preferably from a zip file asset, but not necessarily.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static String getAssetString(String assetPath) {
+               return getAssetString(assetPath, false);
+       }
+
+       /**
+        * Gets the asset, preferably from a zip file asset, but not necessarily.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static InputStream getAssetStream(String assetPath) {
+               return getAssetStream(assetPath, false);
+       }
+
+       /**
+        * Gets the asset from a zip file.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static byte[] getAssetBytesFromZip(String assetPath) {
+               return getAssetBytes(assetPath, true);
+       }
+
+       /**
+        * Gets the asset from a zip file.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static String getAssetStringFromZip(String assetPath) {
+               return getAssetString(assetPath, true);
+       }
+
+       /**
+        * Gets the asset from a zip file.
+        * 
+        * @param assetPath
+        * @return
+        */
+       public static InputStream getAssetStreamFromZip(String assetPath) {
+               return getAssetStream(assetPath, true);
+       }
+
+
+       /**
+        * Get the contents of a path from a zip file asset as byte[], optionally loading
+        * the resource directly using a class loader.
+        * 
+        * @param path
+        * @param zipOnly
+        * @return
+        */
+       private static byte[] getAssetBytes(String path, boolean zipOnly) {
+               byte[] bytes = null;
+               try {
+                       URL url = getInstance()._getURLFromPath(path, true);
+                       if (url == null && !zipOnly) {
+                               url = getAbsoluteURL(path);
+                               //url = Assets.class.getResource(path);
+                       }
+                       if (url == null)
+                               return null;
+                       if (isJS) {
+                               bytes = jsutil.getURLBytes(url);
+                               if (bytes == null) {
+                                       url.openStream();
+                                       bytes = jsutil.getURLBytes(url);
+                               }
+                       } else {
+                               bytes = getLimitedStreamBytes(url.openStream(), -1, null);
+                       }
+               } catch (Throwable t) {
+                       t.printStackTrace();
+               }
+               return bytes;
+       }
+
+       /**
+        * Get the contents of a path from a zip file asset as a String, optionally
+        * loading the resource directly using a class loader.
+        * 
+        * @param path
+        * @param zipOnly
+        * @return
+        */
+       private static String getAssetString(String path, boolean zipOnly) {
+               byte[] bytes = getAssetBytes(path, zipOnly);
+               return (bytes == null ? null : new String(bytes));
+       }
+
+       /**
+        * Get the contents of a path from a zip file asset as an InputStream, optionally
+        * loading the resource directly using a class loader.
+        * 
+        * @param path
+        * @param zipOnly
+        * @return
+        */
+       private static InputStream getAssetStream(String path, boolean zipOnly) {
+               try {
+                       URL url = getInstance()._getURLFromPath(path, true);
+                       if (url == null && !zipOnly) {
+                               url = Assets.class.getClassLoader().getResource(path);
+                       }
+                       if (url != null)
+                               return url.openStream();
+               } catch (Throwable t) {
+               }
+               return null;
+       }
+       /**
+        * Determine the path to an asset. If not found in a zip file asset, return the
+        * absolute path to this resource.
+        * 
+        * @param fullPath
+        * @return
+        */
+       public static URL getURLFromPath(String fullPath) {
+               return getInstance()._getURLFromPath(fullPath, false);
+       }
+
+       /**
+        * Determine the path to an asset. If not found in a zip file asset, optionally
+        * return null or the absolute path to this resource.
+        * 
+        * @param fullPath
+        * @param zipOnly
+        * @return the URL to this asset, or null if not found.
+        */
+       public static URL getURLFromPath(String fullPath, boolean zipOnly) {
+               return getInstance()._getURLFromPath(fullPath, zipOnly);
+       }
+
+       private URL _getURLFromPath(String fullPath, boolean zipOnly) {
+               URL url = null;
+               try {
+                       if (fullPath.startsWith("/"))
+                               fullPath = fullPath.substring(1);
+                       for (int i = sortedList.length; --i >= 0;) {
+                               if (fullPath.startsWith(sortedList[i])) {
+                                       url = assetsByPath.get(sortedList[i]).getURL(fullPath);
+                                       ZipEntry ze = findZipEntry(url);
+                                       if (ze == null)
+                                               break;
+                                       if (isJS) {
+                                               jsutil.setURLBytes(url, jsutil.getZipBytes(ze));
+                                       }
+                                       return url;
+                               }
+                       }
+                       if (!zipOnly)
+                               return getAbsoluteURL(fullPath);
+               } catch (MalformedURLException e) {
+               }
+               return null;
+       }
+
+       public static ZipEntry findZipEntry(URL url) {
+               String[] parts = getJarURLParts(url.toString());
+               if (parts == null || parts[0] == null || parts[1].length() == 0)
+                       return null;
+               return findZipEntry(parts[0], parts[1]);
+       }
+
+       public static ZipEntry findZipEntry(String zipFile, String fileName) {
+               return getZipContents(zipFile).get(fileName);
+       }
+
+       /**
+        * Gets the contents of a zip file.
+        * 
+        * @param zipPath the path to the zip file
+        * @return a set of file names in alphabetical order
+        */
+       public static Map<String, ZipEntry> getZipContents(String zipPath) {
+               return getInstance()._getZipContents(zipPath);
+       }
+
+       private Map<String, ZipEntry> _getZipContents(String zipPath) {
+               URL url = getURLWithCachedBytes(zipPath); // BH carry over bytes if we have them already
+               Map<String, ZipEntry> fileNames = htZipContents.get(url.toString());
+               if (fileNames != null)
+                       return fileNames;
+               try {
+                       // Scan URL zip stream for files.
+                       return readZipContents(url.openStream(), url);
+               } catch (Exception ex) {
+                       ex.printStackTrace();
+                       return null;
+               }
+       }
+
+       /**
+        * Deconstruct a jar URL into two parts, before and after "!/".
+        * 
+        * @param source
+        * @return
+        */
+       public static String[] getJarURLParts(String source) {
+               int n = source.indexOf("!/");
+               if (n < 0)
+                       return null;
+               String jarfile = source.substring(0, n).replace("jar:", "");
+               while (jarfile.startsWith("//"))
+                       jarfile = jarfile.substring(1);
+               return new String[] { jarfile, (n == source.length() - 2 ? null : source.substring(n + 2)) };
+       }
+
+       /**
+        * Get the contents of any URL as a byte array. This method does not do any asset check. It just gets the url data as a byte array.
+        * 
+        * @param url
+        * @return byte[]
+        * 
+        * @author hansonr
+        */
+       public static byte[] getURLContents(URL url) {
+               if (url == null)
+                       return null;
+               try {
+                       if (isJS) {
+                               // Java 9! return new String(url.openStream().readAllBytes());
+                               return jsutil.readAllBytes(url.openStream());
+                       }
+                       return getLimitedStreamBytes(url.openStream(), -1, null);
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+               return null;
+       }
+
+       /**
+        * 
+        * Convert a file path to a URL, retrieving any cached file data, as from DnD.
+        * Do not do any actual data transfer. This is a swingjs.JSUtil service.
+        * 
+        * @param path
+        * @return
+        */
+       private static URL getURLWithCachedBytes(String path) {
+               URL url = getAbsoluteURL(path);
+               if (url != null)
+                       addJSCachedBytes(url);
+               return url;
+       }
+
+       private Map<String, ZipEntry> readZipContents(InputStream is, URL url) throws IOException {
+               HashMap<String, ZipEntry> fileNames = new HashMap<String, ZipEntry>();
+               if (doCacheZipContents)
+                       htZipContents.put(url.toString(), fileNames);
+               ZipInputStream input = new ZipInputStream(is);
+               ZipEntry zipEntry = null;
+               int n = 0;
+               while ((zipEntry = input.getNextEntry()) != null) {
+                       if (zipEntry.isDirectory() || zipEntry.getSize() == 0)
+                               continue;
+                       n++;
+                       String fileName = zipEntry.getName();
+                       fileNames.put(fileName, zipEntry); // Java has no use for the ZipEntry, but JavaScript can read it.
+               }
+               input.close();
+               System.out.println("Assets: " + n + " zip entries found in " + url); //$NON-NLS-1$
+               return fileNames;
+       }
+
+       private void resort() {
+               sortedList = new String[assetsByPath.size()];
+               int i = 0;
+               for (String path : assetsByPath.keySet()) {
+                       sortedList[i++] = path;
+               }
+               Arrays.sort(sortedList);
+       }
+
+
+       /**
+        * Only needed for Java
+        * 
+        * @param is
+        * @param n
+        * @param out
+        * @return
+        * @throws IOException
+        */
+       private static byte[] getLimitedStreamBytes(InputStream is, long n, OutputStream out) throws IOException {
+
+               // Note: You cannot use InputStream.available() to reliably read
+               // zip data from the web.
+
+               boolean toOut = (out != null);
+               int buflen = (n > 0 && n < 1024 ? (int) n : 1024);
+               byte[] buf = new byte[buflen];
+               byte[] bytes = (out == null ? new byte[n < 0 ? 4096 : (int) n] : null);
+               int len = 0;
+               int totalLen = 0;
+               if (n < 0)
+                       n = Integer.MAX_VALUE;
+               while (totalLen < n && (len = is.read(buf, 0, buflen)) > 0) {
+                       totalLen += len;
+                       if (toOut) {
+                               out.write(buf, 0, len);
+                       } else {
+                               if (totalLen > bytes.length)
+                                       bytes = Arrays.copyOf(bytes, totalLen * 2);
+                               System.arraycopy(buf, 0, bytes, totalLen - len, len);
+                               if (n != Integer.MAX_VALUE && totalLen + buflen > bytes.length)
+                                       buflen = bytes.length - totalLen;
+                       }
+               }
+               if (toOut)
+                       return null;
+               if (totalLen == bytes.length)
+                       return bytes;
+               buf = new byte[totalLen];
+               System.arraycopy(bytes, 0, buf, 0, totalLen);
+               return buf;
+       }
+
+       /**
+        * Return all assets in the form that is appropriate for the Info.assets value in SwingJS.
+        * 
+        */
+       @Override
+       public String toString() {
+               String s = "[";
+               for (int i = 0; i < sortedList.length; i++) {
+                       Asset a = assetsByPath.get(sortedList[i]);
+                       s += (i == 0 ? "" : ",") + a;
+               }
+               return s + "]";
+       }
+
+}
diff --git a/src/javajs/async/Async.java b/src/javajs/async/Async.java
new file mode 100644 (file)
index 0000000..f6f31e5
--- /dev/null
@@ -0,0 +1,189 @@
+package javajs.async;
+
+/**
+ * A package to manage asynchronous aspects of SwingJS
+ * 
+ * The javajs.async package simplifies the production of methods that can be
+ * used equally well in Java and in JavaScript for handling "pseudo-modal"
+ * blocking in JavaScript, meaning the user is locked out of other interactions,
+ * as in Java, but the code is not actually blocking the thread.
+ * 
+ * Included in this package are
+ * 
+ * Async
+ * 
+ * Provides few simple generic static methods.
+ * 
+ * Async.isJS() -- true if we are running this in JavaScript
+ * 
+ * Async.javaSleep() -- bypassing Thread.sleep() for JavaScript; allowing it for
+ * Java
+ * 
+ * 
+ * AsyncDialog
+ * 
+ * Provides several very useful methods that act as a replacement for direct
+ * JOptionPane or JDialog calls, including both a synchronous callback
+ * (manifested in Java) and an asynchronous callback (JavaScript), both
+ * resulting in the same effect, except for the fact that the JavaScript code
+ * has returned immediately from the call with an "ignore me" reference, while
+ * Java is waiting at that call to return a value from JOptionPane (which is
+ * saved by AsyncDialog and not delivered as a return value).
+ * 
+ * AsyncDialog does not extend JOptionPane, but it mirrors that classes public
+ * methods. There are a LOT of public methods in JOPtionPane. I suppose we
+ * should implement them all. In practice, AsyncDialog calls standard
+ * JOptionPane static classes for the dialogs.
+ * 
+ * Initially, the following methods are implemented:
+ * 
+ * public void showConfirmDialog(Component frame, Object message, String title,
+ * ActionListener a)
+ * 
+ * public void showConfirmDialog(Component frame, Object message, String title,
+ * int optionType, ActionListener a)
+ * 
+ * public void showConfirmDialog(Component frame, Object message, String title,
+ * int optionType, int messageType, ActionListener a)
+ * 
+ * public void showInputDialog(Component frame, Object message, ActionListener
+ * a)
+ * 
+ * public void showInputDialog(Component frame, Object message, String title,
+ * int messageType, Icon icon, Object[] selectionValues, Object
+ * initialSelectionValue, ActionListener a)
+ * 
+ * public void showMessageDialog(Component frame, Object message, ActionListener
+ * a)
+ * 
+ * public void showOptionDialog(Component frame, Object message, String title,
+ * int optionType, int messageType, Icon icon, Object[] options, Object
+ * initialValue, ActionListener a)
+ * 
+ * 
+ * All nonstatic methods, requiring new AsyncDialog(), also require an
+ * ActionListener. This listener will get a call to actionPerformed(ActionEvent)
+ * where:
+ * 
+ * event.getSource() is a reference to the originating AsyncDialog (super
+ * JOptionPane) for all information that a standard JOptionPane can provide,
+ * along with the two methods int getOption() and Object getChoice().
+ * 
+ * event.getID() is a reference to the standard JOptionPane int return code.
+ * 
+ * event.getActionCommand() also holds a value, but it may or may not be of
+ * value.
+ * 
+ * 
+ * A few especially useful methods are static, allowing just one or two expected
+ * callbacks of interest:
+ * 
+ * AsyncDialog.showOKAsync(Component parent, Object message, String title,
+ * Runnable ok)
+ * 
+ * AsyncDialog.showYesAsync (Component parent, Object message, String title,
+ * Runnable yes)
+ * 
+ * AsyncDialog.showYesNoAsync (Component parent, Object message, String title,
+ * Runnable yes, Runnable no)
+ * 
+ * These methods provide a fast way to adjust JOptionPane calls to be
+ * asynchronous.
+ * 
+ * 
+ * 
+ * AsyncFileChooser extends javax.swing.JFileChooser
+ * 
+ * Accepted constructors include:
+ * 
+ * public AsyncFileChooser()
+ * 
+ * public AsyncFileChooser(File file)
+ * 
+ * public AsyncFileChooser(File file, FileSystemView view)
+ * 
+ * (Note, however, that FileSystemView has no equivalent in JavaScript.)
+ * 
+ * It's three public methods include:
+ * 
+ * public void showDialog(Component frame, String btnLabel, Runnable ok,
+ * Runnable cancel)
+ * 
+ * public void showOpenDialog(Component frame, Runnable ok, Runnable cancel)
+ * 
+ * public void showSaveDialog(Component frame, Runnable ok, Runnable cancel)
+ * 
+ * 
+ * ActionListener is not needed here, as the instance of new AsyncFileChooser()
+ * already has direct access to all the JFileChooser public methods such as
+ * getSelectedFile() and getSelectedFiles().
+ * 
+ * As a subclass of JFileChooser, it accepts all three public showXXXX methods
+ * of JFileChooser, namely:
+ * 
+ * public void showDialog(Component frame, String btnLabel)
+ * 
+ * public void showOpenDialog(Component frame)
+ * 
+ * public void showSaveDialog(Component frame)
+ * 
+ * 
+ * None of these are recommended. AsyncFileChooser will indicate errors if the
+ * first of these two are called. (showSaveDialog is fine, as it is modal even
+ * in JavaScript. However it is not recommended that showSaveDialog(Component
+ * frame) be used, as in the future browsers may implement some sort of file
+ * saver in HTML5.
+ * 
+ * 
+ * 
+ * AsyncColorChooser
+ * 
+ * 
+ * AsyncColorChooser accesses JColorChooser asynchronously, using a private
+ * SwingJS setting that tells JColorChooser to report back to it with property
+ * changes. It is constructed using new AsyncColorChooser() and implements just
+ * two methods:
+ * 
+ * public void showDialog(Component component, String title, Color initialColor,
+ * ActionListener listener)
+ * 
+ * public Color getSelectedColor()
+ * 
+ * 
+ * The listener will get an actionPerformed(ActionEvent) callback with
+ * event.getID() equal to the color value or 0 if canceled. The
+ * getSelectedColor() method may also be called from this callback to retrieve
+ * the associated java.awt.Color object, using
+ * 
+ * ((AsyncColorChooser)e.getSource()).getSelectedColor()
+ * 
+ * As in Java, a null value for the selected color indicates that the
+ * JColorChooser was closed.
+ * 
+ * Bob Hanson 2019.11.07
+ * 
+ * 
+ * @author Bob Hanson hansonr_at_stolaf.edu
+ *
+ */
+public class Async {
+
+       public static boolean isJS() {
+               return  (/** @j2sNative 1 ? true : */false);
+       }
+
+       /**
+        * No sleep in JavaScript
+        * @param ms
+        */
+       public static void javaSleep(int ms) {
+               if (!isJS()) {
+                       try {
+                               Thread.sleep(ms);
+                       } catch (InterruptedException e) {
+                       }
+               }
+       
+       }
+
+}
diff --git a/src/javajs/async/AsyncColorChooser.java b/src/javajs/async/AsyncColorChooser.java
new file mode 100644 (file)
index 0000000..2474833
--- /dev/null
@@ -0,0 +1,67 @@
+package javajs.async;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JColorChooser;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A simple Asynchronous file chooser for JavaScript; synchronous with Java.
+ * 
+ * Allows two modes -- using an ActionListener (setAction(ActionListener) or constructor(ActionListener))
+ * 
+ * @author Bob Hanson
+ */
+
+public class AsyncColorChooser implements PropertyChangeListener {
+
+       private ActionListener listener;
+       private Color selectedColor;
+
+       public void showDialog(Component component, String title, Color initialColor, ActionListener listener) {
+               setListener(listener);
+               process(JColorChooser.showDialog(component, title, initialColor));
+               unsetListener();
+       }
+
+       public Color getSelectedColor() {
+               return selectedColor;
+       }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               // JavaScript only
+               Color c = (Color) evt.getNewValue();
+               
+               switch (evt.getPropertyName()) {
+               case "SelectedColor":
+                       process(c);
+                       break;
+               }
+       }
+
+       private void setListener(ActionListener a) {
+               listener = a;
+               /** @j2sNative Clazz.load("javax.swing.JColorChooser");javax.swing.JColorChooser.listener = this */
+       }
+
+       private void unsetListener() {
+               /** @j2sNative javax.swing.JColorChooser.listener = null */
+       }
+
+       
+       
+       private void process(Color c) {
+               if (c instanceof UIResource)
+                       return;
+               selectedColor = c;
+               listener.actionPerformed(new ActionEvent(this, c == null ? 0 : c.getRGB(), c == null ? null : c.toString()));
+       }
+       
+}
diff --git a/src/javajs/async/AsyncDialog.java b/src/javajs/async/AsyncDialog.java
new file mode 100644 (file)
index 0000000..5752f48
--- /dev/null
@@ -0,0 +1,282 @@
+package javajs.async;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.Icon;
+import javax.swing.JOptionPane;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A class to manage asynchronous input, option, and confirmation dialogs.
+ * 
+ * @author Bob Hanson hansonr_at_stolaf.edu
+ *
+ */
+public class AsyncDialog implements PropertyChangeListener {
+
+// see discussion in net.sf.j2s.core/doc/Differences.txt
+//
+// Confirmation dialog example. Note moving the parent component into the constructor.
+// Original:
+//
+//             private void promptQuit() {
+//                     int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//                     switch (sel) {
+//                     case JOptionPane.YES_OPTION:
+//                             resultsTab.clean();
+//                             seqs.dispose();
+//                             if (fromMain) {
+//                                     System.exit(0);
+//                             }
+//                             break;
+//                     }
+//             }
+//
+// revised: 
+//
+//             private void promptQuitAsync() {
+//                     new AsyncDialog().showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION, new ActionListener() {
+//
+//     @Override
+//     public void actionPerformed(ActionEvent e) {
+//         int sel = ((AsyncDialog)e.getSource()).getOption();
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }}));
+//             }
+
+       
+       public AsyncDialog() {
+       }
+       
+       private ActionListener actionListener;
+       private Object choice;
+       private Object[] options;
+       private Object value;
+       private boolean wantsInput;
+
+       // These options can be supplemented as desired.
+
+       
+       /**
+        * Synchronous call; OK in JavaScript as long as we are using a JavaScript prompt() call
+        * 
+        * @param frame
+        * @param msg
+        * @return
+        */
+       @Deprecated
+       public static String showInputDialog(Component frame, String msg) {
+               return JOptionPane.showInputDialog(frame, msg);
+       }
+
+       public void showInputDialog(Component frame, Object message, ActionListener a) {
+               setListener(a);
+               wantsInput = true;
+               process(JOptionPane.showInputDialog(frame, message));
+               unsetListener();
+       }
+
+       public void showInputDialog(Component frame, Object message, String title, int messageType, Icon icon,
+                       Object[] selectionValues, Object initialSelectionValue, ActionListener a) {
+               setListener(a);
+               wantsInput = true;
+               process(JOptionPane.showInputDialog(frame, message, title, messageType, icon, selectionValues,
+                               initialSelectionValue));
+               unsetListener();
+       }
+
+       public void showMessageDialog(Component frame, Object message, ActionListener a) {
+               setListener(a);
+               JOptionPane.showMessageDialog(frame, message);
+               unsetListener();
+               if (/** @j2sNative false || */true)
+                       process("" + message);
+       }
+
+       public void showOptionDialog(Component frame, Object message, String title, int optionType, int messageType,
+                       Icon icon, Object[] options, Object initialValue, ActionListener a) {
+               actionListener = a;
+               this.options = options;
+               setListener(a);
+               process(JOptionPane.showOptionDialog(frame, message, title, optionType, messageType, icon, options,
+                               initialValue));
+               unsetListener();
+       }
+
+       public void showConfirmDialog(Component frame, Object message, String title, ActionListener a) {
+               showConfirmDialog(frame, message, title, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, a);
+       }
+
+       public void showConfirmDialog(Component frame, Object message, String title, int optionType, ActionListener a) {
+               showConfirmDialog(frame, message, title, optionType, JOptionPane.QUESTION_MESSAGE, a);
+       }
+
+       public void showConfirmDialog(Component frame, Object message, String title, int optionType, int messageType,
+                       ActionListener a) {
+               setListener(a);
+               process(JOptionPane.showConfirmDialog(frame, message, title, optionType, messageType));
+               unsetListener();
+       }
+
+       /**
+        * retrieve selection from the ActionEvent, for which "this" is getSource()
+        * 
+        * @return
+        */
+       public Object getChoice() {
+               return choice;
+       }
+
+       public int getOption() {
+               if (!(choice instanceof Integer)) {
+                       throw new java.lang.IllegalArgumentException("AsyncDialog.getOption called for non-Integer choice");
+               }
+               return ((Integer) choice).intValue();
+       }
+
+       /**
+        * A dialog option that allows for YES, NO, and CLOSE options via
+        * ActionListener. ActionEvent.getID() contains the reply.
+        * 
+        * @param parent   The parent component for the dialog
+        * @param message  The text of the message to display
+        * @param title    Optional title defaults to "Question"
+        * @param listener Handle options based on an ActionEvent
+        */
+       public static void showYesNoAsync(Component parent, Object message, String title, ActionListener listener) {
+               new AsyncDialog().showConfirmDialog(parent, message, (title == null ? "Question" : title),
+                               JOptionPane.YES_NO_OPTION, listener);
+       }
+
+       /**
+        * A dialog option that involves just a YES follower.
+        * @param parent
+        * @param message
+        * @param title TODO
+        * @param yes
+        */
+       public static void showYesAsync(Component parent, Object message, String title, Runnable yes) {
+               AsyncDialog.showYesNoAsync(parent, message, title, new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (e.getID() == JOptionPane.YES_OPTION) {
+                                       yes.run();
+                               }
+                       }
+                       
+               });
+       }
+
+       /**
+        * A dialog option that involves just an OK follower.
+        * @param parent
+        * @param message
+        * @param title
+        * @param ok
+        */
+       public static void showOKAsync(Component parent, Object message, String title, Runnable ok) {
+               new AsyncDialog().showConfirmDialog(parent, message, title, JOptionPane.OK_CANCEL_OPTION, new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (e.getID() == JOptionPane.OK_OPTION) {
+                                       ok.run();
+                               }
+                       }
+                       
+               });
+       }
+
+
+       private void setListener(ActionListener a) {
+               actionListener = a;
+               @SuppressWarnings("unused")
+               Class c = JOptionPane.class; // loads the class
+               /** @j2sNative c.$clazz$.listener = this */
+       }
+
+       private void unsetListener() {
+               /** @j2sNative javax.swing.JOptionPane.listener = null */
+       }
+
+       /**
+        * Switch from property change to action.
+        * 
+        */
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               value = evt.getNewValue();
+               switch (evt.getPropertyName()) {
+               case "inputValue":                      
+                       process(value);
+                       break;
+               case "value":
+                       if (value != null && options == null && !(value instanceof Integer)) {
+                               process(getOptionIndex(((JOptionPane) evt.getSource()).getOptions(), value));
+                               return;
+                       }
+                       if (options != null) {
+                               int i = getOptionIndex(options, value);
+                               value = Integer.valueOf(i >= 0 ? i : JOptionPane.CLOSED_OPTION);
+                       } 
+                       process(value);
+                       break;
+               }
+       }
+
+       private int getOptionIndex(Object[] options, Object val) {
+               if (options != null)
+                       for (int i = 0; i < options.length; i++) {
+                               if (options[i] == val)
+                                       return i;
+                       }
+               return -1;
+       }
+
+       public Object getValue() {
+               if (wantsInput || options == null)
+                       return value;
+               int val = ((Integer) value).intValue();
+               return (val < 0 ? null : options[val]);
+       }
+       
+       private boolean processed;
+
+       /**
+        * Return for confirm dialog.
+        * 
+        * @param ret may be JavaScript NaN, testable as ret != ret or ret != - -ret
+        */
+       private void process(int ret) {
+               if (ret != -(-ret) || processed)
+                       return;
+               processed = true;
+               choice = ret;
+               actionListener.actionPerformed(new ActionEvent(this, ret, "SelectedOption"));
+       }
+
+       private void process(Object ret) {
+               if (ret instanceof UIResource || processed)
+                       return;
+               processed = true;
+               choice = ret;
+               actionListener.actionPerformed(new ActionEvent(this, 
+                               ret == null ? JOptionPane.CANCEL_OPTION : JOptionPane.OK_OPTION, 
+                                               (ret == null ? null : ret.toString())));
+       }
+
+
+}
diff --git a/src/javajs/async/AsyncFileChooser.java b/src/javajs/async/AsyncFileChooser.java
new file mode 100644 (file)
index 0000000..849fe83
--- /dev/null
@@ -0,0 +1,217 @@
+package javajs.async;
+
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.function.Function;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.filechooser.FileSystemView;
+
+/**
+ * A simple Asynchronous file chooser for JavaScript and Java.
+ * 
+ * Requires an OK runnable; JavaScript can return notification of cancel for
+ * file reading only, not saving.
+ * 
+ * @author Bob Hanson
+ */
+
+public class AsyncFileChooser extends JFileChooser implements PropertyChangeListener {
+
+       private int optionSelected;
+       private Runnable ok, cancel; // sorry, no CANCEL in JavaScript for file open
+       private boolean isAsyncSave = true;
+       private static boolean notified;
+
+       public AsyncFileChooser() {
+               super();
+       }
+
+       public AsyncFileChooser(File file) {
+               super(file);
+       }
+
+       public AsyncFileChooser(File file, FileSystemView view) {
+               super(file, view);
+       }
+
+       @Deprecated
+       @Override
+       public int showDialog(Component frame, String btnText) {
+               // This one can come from JFileChooser - default is OPEN
+               return super.showDialog(frame, btnText);
+       }
+
+       private int err() {
+               try {
+                       throw new java.lang.IllegalAccessException("Warning! AsyncFileChooser interface bypassed!");
+               } catch (IllegalAccessException e) {
+                       e.printStackTrace();
+               }
+               return JFileChooser.ERROR_OPTION;
+       }
+
+       @Deprecated
+       @Override
+       public int showOpenDialog(Component frame) {
+               return err();
+       }
+
+       @Override
+       public int showSaveDialog(Component frame) {
+               isAsyncSave  = false;
+               return super.showSaveDialog(frame);
+       }
+
+       /**
+        * 
+        * @param frame
+        * @param btnLabel "open" or "save"
+        * @param ok
+        * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
+        */
+       public void showDialog(Component frame, String btnLabel, Runnable ok, Runnable cancel) {
+               this.ok = ok;
+               if (getDialogType() != JFileChooser.SAVE_DIALOG && cancel != null)
+                       notifyCancel();
+               process(super.showDialog(frame, btnLabel));
+       }
+
+       /**
+        * 
+        * @param frame
+        * @param ok
+        * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
+        */
+       public void showOpenDialog(Component frame, Runnable ok, Runnable cancel) {
+               this.ok = ok;
+               if (cancel != null)
+                       notifyCancel();
+               process(super.showOpenDialog(frame));
+       }
+
+       /**
+        * 
+        * This just completes the set. It is not necessary for JavaScript, because JavaScript
+        * will just throw up a simple modal OK/Cancel message anyway.
+        * 
+        * @param frame
+        * @param ok
+        * @param cancel must be null
+        */
+       public void showSaveDialog(Component frame, Runnable ok, Runnable cancel) {
+               this.ok = ok;
+               this.cancel = cancel;
+               process(super.showSaveDialog(frame));
+       }
+
+       
+       /**
+        * Locate a file for input or output. Note that JavaScript will not return on cancel for OPEN_DIALOG.
+        * 
+        * @param title       The title for the dialog
+        * @param mode        OPEN_DIALOG or SAVE_DIALOG
+        * @param processFile function to use when complete
+        */
+         public static void getFileAsync(Component parent, String title, int mode, Function<File, Void> processFile) {
+                 // BH no references to this method. So changing its signature for asynchonous use
+                 // And it didn't do as advertised - ran System.exit(0) if canceled
+           // create and display a file dialog
+               AsyncFileChooser fc = new AsyncFileChooser();
+               fc.setDialogTitle(title);
+               Runnable after = new Runnable() {
+
+                       @Override
+                       public void run() {
+                               processFile.apply(fc.getSelectedFile());
+                       }
+       
+               };
+               if (mode == JFileChooser.OPEN_DIALOG) {
+                       fc.showOpenDialog(parent, after, after);  
+               } else {
+                       fc.showSaveDialog(parent, after, after);  
+               }
+                               
+         }
+           
+               /**
+                * Run yes.run() if a file doesn't exist or if the user allows it, else run no.run()
+                * @param parent
+                * @param filename
+                * @param title
+                * @param yes (approved)
+                * @param no (optional)
+                */
+               public static void checkReplaceFileAsync(Component parent, File outfile, String title, Runnable yes, Runnable no) {
+                       if (outfile.exists()) {
+                               AsyncDialog.showYesNoAsync(parent,
+                                               outfile + " exists. Replace it?", null, new ActionListener() {
+               
+                                                       @Override
+                                                       public void actionPerformed(ActionEvent e) {
+                                                               switch (e.getID()) {
+                                                               case JOptionPane.YES_OPTION:
+                                                                       yes.run();
+                                                                       break;
+                                                               default:
+                                                                       if (no != null)
+                                                                               no.run();
+                                                                       break;
+                                                               }
+                                                       }
+               
+                                               });
+               
+                       } else {
+                               yes.run();
+                       }
+               
+               }
+
+       private void notifyCancel() {
+               if (!notified) {
+                       System.err.println("developer note: JavaScript cannot fire a FileChooser CANCEL action");
+               }
+               notified = true;
+       }
+
+       @Override
+       public void propertyChange(PropertyChangeEvent evt) {
+               switch (evt.getPropertyName()) {
+               case "SelectedFile":
+               case "SelectedFiles":
+                       process(optionSelected = (evt.getNewValue() == null ? CANCEL_OPTION : APPROVE_OPTION));
+                       break;
+               }
+       }
+
+       private void process(int ret) {
+               if (ret != -(-ret))
+                       return; // initial JavaScript return is NaN
+               optionSelected = ret;
+               File f = getSelectedFile();
+               if (f == null) {
+                       if (cancel != null)
+                               cancel.run();
+               } else {
+                       if (ok != null)
+                               ok.run();
+               }
+       }
+
+       public int getSelectedOption() {
+               return optionSelected;
+       }
+
+       public static byte[] getFileBytes(File f) {
+               return /** @j2sNative f.秘bytes || */null;
+       }
+
+}
diff --git a/src/javajs/async/AsyncSwingWorker.java b/src/javajs/async/AsyncSwingWorker.java
new file mode 100644 (file)
index 0000000..703f7f5
--- /dev/null
@@ -0,0 +1,431 @@
+package javajs.async;
+
+import java.awt.Component;
+
+import javax.swing.ProgressMonitor;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+
+import javajs.async.SwingJSUtils.StateHelper;
+import javajs.async.SwingJSUtils.StateMachine;
+
+/**
+ * v. 2020.06.03 
+ * 
+ * Executes synchronous or asynchronous tasks using a SwingWorker in Java or
+ * JavaScript, equivalently.
+ * 
+ * Unlike a standard SwingWorker, AsyncSwingWorker may itself be asynchronous.
+ * For example, it might load a file asynchronously, or carry out a background
+ * process in JavaScript much like one might be done in Java, but with only a
+ * single thread.
+ * 
+ * Whereas a standard SwingWorker would execute done() long before the
+ * asynchronous task completed, this class will wait until progress has been
+ * asynchronously set greater or equal to its max value or the task is canceled
+ * before executing that method.
+ * 
+ * Three methods must be supplied by the subclass:
+ * 
+ * void initAsync()
+ * 
+ * int doInBackgroundAsync(int progress)
+ * 
+ * void doneAsync()
+ * 
+ * Both initAsync() and doneAsync() are technically optional - they may be
+ * empty. doInBackgroundAsync(), however, is the key method where, like
+ * SwingWorker's doInBackground, the main work is done. The supplied progress
+ * parameter reminds the subclass of where it is at, and the return value allows
+ * the subclass to update the progress field in both the SwingWorker and the
+ * ProgressMonitor.
+ * 
+ * If it is desired to run the AsyncSwingWorker synchronously, call the
+ * executeSynchronously() method rather than execute(). Never call
+ * SwingWorker.run().
+ * 
+ * Note that doInBackgroundAsync runs on the Java AWT event queue. This means
+ * that, unlike a true SwingWorker, it will run in event-queue sequence, after
+ * anything that that method itself adds to the queue. This is what SwingWorker itself
+ * does with its done() signal. 
+ * 
+ * If doInBackgroundAsync has tasks that are time intensive, the thing to do is to
+ * 
+ * (a) pause this worker by setting the value of progress for the NEXT step:
+ * 
+ *    setProgressAsync(n);
+ *    
+ * (b) pause the timer so that when doInBackgroundAsync returns, the timer is not fired:
+ * 
+ *    setPaused(true);
+ * 
+ * (c) start your process as new Thread, which bypasses the AWT EventQueue:
+ * 
+ *    new Thread(Runnable).start();
+ *    
+ * (d) have your thread, when it is done, return control to this worker:
+ * 
+ *    setPaused(false);
+ *    
+ * This final call restarts the worker with the currently specified progress value.
+ * 
+ * @author hansonr
+ *
+ */
+public abstract class AsyncSwingWorker extends SwingWorker<Void, Void> implements StateMachine {
+
+
+       // PropertyChangeEvent getPropertyName()
+       
+       private static final String PROPERTY_STATE = "state";
+       private static final String PROPERTY_PAUSE = "pause";
+       
+       // PropertyChangeEvent getNewValue()
+       
+       public static final String STARTED_ASYNC = "STARTED_ASYNC";
+       public static final String STARTED_SYNC = "STARTED_SYNC";
+       
+       public static final String DONE_ASYNC = "DONE_ASYNC";
+       public static final String CANCELED_ASYNC = "CANCELED_ASYNC";
+       
+       public static final String PAUSED = "PAUSED";
+       public static final String RESUMED = "RESUMED";
+
+       protected int progressAsync;
+
+       /**
+        * Override to provide initial tasks.
+        */
+       abstract public void initAsync();
+
+       /**
+        * Given the last progress, do some portion of the task that the SwingWorker
+        * would do in the background, and return the new progress. returning max or
+        * above will complete the task.
+        * 
+        * @param progress
+        * @return new progress
+        */
+       abstract public int doInBackgroundAsync(int progress);
+
+       /**
+        * Do something when the task is finished or canceled.
+        * 
+        */
+       abstract public void doneAsync();
+
+       protected ProgressMonitor progressMonitor;
+
+       protected int delayMillis;
+       protected String note;
+       protected int min;
+       protected int max;
+       protected int progressPercent;
+
+       protected boolean isAsync;
+       private Exception exception;
+
+       /**
+        * Construct an asynchronous SwingWorker task that optionally will display a
+        * ProgressMonitor. Progress also can be monitored by adding a
+        * PropertyChangeListener to the AsyncSwingWorker and looking for the "progress"
+        * event, just the same as for a standard SwingWorker.
+        * 
+        * @param owner       optional owner for the ProgressMonitor, typically a JFrame
+        *                    or JDialog.
+        * 
+        * @param title       A non-null title indicates we want to use a
+        *                    ProgressMonitor with that title line.
+        * 
+        * @param delayMillis A positive number indicating the delay we want before
+        *                    executions, during which progress will be reported.
+        * 
+        * @param min         The first progress value. No range limit.
+        * 
+        * @param max         The last progress value. No range limit; may be greater
+        *                    than min.
+        * 
+        */
+       public AsyncSwingWorker(Component owner, String title, int delayMillis, int min, int max) {
+               if (title != null && delayMillis > 0) {
+                       progressMonitor = new ProgressMonitor(owner, title, "", Math.min(min, max), Math.max(min, max));
+                       progressMonitor.setProgress(Math.min(min, max)); // displays monitor
+               }
+               this.delayMillis = Math.max(0, delayMillis);
+               this.isAsync = (delayMillis > 0);
+
+               this.min = min;
+               this.max = max;
+       }
+
+       public void executeAsync() {
+               firePropertyChange(PROPERTY_STATE, null, STARTED_ASYNC);
+               super.execute();
+       }
+
+       public void executeSynchronously() {
+               firePropertyChange(PROPERTY_STATE, null, STARTED_SYNC);
+               isAsync = false;
+               delayMillis = 0;
+               try {
+                       doInBackground();
+               } catch (Exception e) {
+                       exception = e;
+                       e.printStackTrace();
+                       cancelAsync();
+               }
+       }
+
+       public Exception getException() {
+               return exception;
+       }
+
+       public int getMinimum() {
+               return min;
+       }
+
+       public void setMinimum(int min) {
+               this.min = min;
+               if (progressMonitor != null) {
+                       progressMonitor.setMinimum(min);
+               }
+       }
+
+       public int getMaximum() {
+               return max;
+       }
+
+       public void setMaximum(int max) {
+               if (progressMonitor != null) {
+                       progressMonitor.setMaximum(max);
+               }
+               this.max = max;
+       }
+
+       public int getProgressPercent() {
+               return progressPercent;
+       }
+
+       public void setNote(String note) {
+               this.note = note;
+               if (progressMonitor != null) {
+                       progressMonitor.setNote(note);
+               }
+       }
+
+       /**
+        * Cancel the asynchronous process.
+        * 
+        */
+       public void cancelAsync() {
+               helper.interrupt();
+       }
+
+       /**
+        * Check to see if the asynchronous process has been canceled.
+        *
+        * @return true if StateHelper is not alive anymore
+        * 
+        */
+       public boolean isCanceledAsync() {
+               return !helper.isAlive();
+       }
+
+       /**
+        * Check to see if the asynchronous process is completely done.
+        * 
+        * @return true only if the StateMachine is at STATE_DONE
+        * 
+        */
+       public boolean isDoneAsync() {
+               return helper.getState() == STATE_DONE;
+       }
+
+       /**
+        * Override to set a more informed note for the ProcessMonitor.
+        * 
+        * @param progress
+        * @return
+        */
+       public String getNote(int progress) {
+               return String.format("Completed %d%%.\n", progress);
+       }
+
+       /**
+        * Retrieve the last note delivered by the ProcessMonitor.
+        * 
+        * @return
+        */
+       public String getNote() {
+               return note;
+       }
+
+       public int getProgressAsync() {
+               return progressAsync;
+       }
+
+       /**
+        * Set the [min,max] progress safely.
+        * 
+        * SwingWorker only allows progress between 0 and 100. This method safely
+        * translates [min,max] to [0,100].
+        * 
+        * @param n
+        */
+       public void setProgressAsync(int n) {
+               n = (max > min ? Math.max(min, Math.min(n, max)) : Math.max(max, Math.min(n, min)));
+               progressAsync = n;
+               n = (n - min) * 100 / (max - min);
+               n = (n < 0 ? 0 : n > 100 ? 100 : n);
+               progressPercent = n;
+       }
+
+       ///// the StateMachine /////
+
+       private final static int STATE_INIT = 0;
+       private final static int STATE_LOOP = 1;
+       private final static int STATE_WAIT = 2;
+       private final static int STATE_DONE = 99;
+       
+       private StateHelper helper;
+
+       protected StateHelper getHelper() {
+               return helper;
+       }
+
+       private boolean isPaused;
+
+       protected void setPaused(boolean tf) {
+               isPaused = tf;
+               firePropertyChange(PROPERTY_PAUSE, null, (tf ? PAUSED : RESUMED));
+               if (!tf)
+                       stateLoop();
+       }
+
+       protected boolean isPaused() {
+               return isPaused;
+       }
+
+       /**
+        * The StateMachine's main loop.
+        * 
+        * Note that a return from this method will exit doInBackground, trigger the
+        * isDone() state on the underying worker, and scheduling its done() for
+        * execution on the AWTEventQueue.
+        *
+        * Since this happens essentially immediately, it is unlikely that
+        * SwingWorker.isCancelled() will ever be true. Thus, the SwingWorker task
+        * itself won't be cancelable in Java or in JavaScript, since its
+        * doInBackground() method is officially complete, and isDone() is true well
+        * before we are "really" done. FutureTask will not set isCancelled() true once
+        * the task has run.
+        * 
+        * We are using an asynchronous task specifically because we want to have the
+        * opportunity for the ProgressMonitor to report in JavaScript. We will have to
+        * cancel our task and report progress explicitly using our own methods.
+        * 
+        */
+       @Override
+       public boolean stateLoop() {
+               while (helper.isAlive() && !isPaused) {
+                       switch (helper.getState()) {
+                       case STATE_INIT:
+                               setProgressAsync(min);
+                               initAsync();
+                               helper.setState(STATE_WAIT);
+                               continue;
+                       case STATE_LOOP:
+                               if (checkCanceled()) {
+                                       helper.setState(STATE_DONE);
+                                       firePropertyChange(PROPERTY_STATE, null, CANCELED_ASYNC);
+                               } else {
+                                       int ret = doInBackgroundAsync(progressAsync);                                   
+                                       if (!helper.isAlive() || isPaused) {
+                                               continue;
+                                       }
+                                       progressAsync = ret;
+                                       setProgressAsync(progressAsync);
+                                       setNote(getNote(progressAsync));
+                                       setProgress(progressPercent);
+                                       if (progressMonitor != null) {
+                                               progressMonitor.setProgress(max > min ? progressAsync : max + min - progressAsync);
+                                       }
+                                       helper.setState(progressAsync == max ? STATE_DONE : STATE_WAIT);
+                               }
+                               continue;
+                       case STATE_WAIT:
+                               // meaning "sleep" and then "loop"
+                               helper.setState(STATE_LOOP);
+                               helper.sleep(delayMillis);
+                               return true;
+                       default:
+                       case STATE_DONE:
+                               stopProgressMonitor();
+                               // Put the doneAsync() method on the AWTEventQueue
+                               // just as for SwingWorker.done().
+                               if (isAsync) {
+                                       SwingUtilities.invokeLater(doneRunnable);
+                               } else {
+                                       doneRunnable.run();
+                               }
+
+                               return false;
+                       }
+               }
+               if (!helper.isAlive()) {
+                       stopProgressMonitor();
+               }
+               return false;
+       }
+
+       private void stopProgressMonitor() {
+               if (progressMonitor != null) {
+                       progressMonitor.close();
+                       progressMonitor = null;
+               }
+       }
+
+       private Runnable doneRunnable = new Runnable() {
+               @Override
+               public void run() {
+                       doneAsync();
+                       firePropertyChange(PROPERTY_STATE, null, DONE_ASYNC);
+               }
+
+       };
+
+       private boolean checkCanceled() {
+               if (isMonitorCanceled() || isCancelled()) {
+                       helper.interrupt();
+                       return true;
+               }
+               return false;
+       }
+
+       //// final SwingWorker methods not to be used by subclasses ////
+
+       private boolean isMonitorCanceled() {
+               return (progressMonitor != null && progressMonitor.isCanceled());
+       }
+
+       /**
+        * see SwingWorker, made final here.
+        * 
+        */
+       @Override
+       final protected Void doInBackground() throws Exception {
+               helper = new StateHelper(this);
+               setProgressAsync(min);
+               helper.next(STATE_INIT);
+               return null;
+       }
+
+       /**
+        * see SwingWorker, made final here. Nothing to do.
+        * 
+        */
+       @Override
+       final public void done() {
+       }
+
+}
diff --git a/src/javajs/async/SwingJSUtils.java b/src/javajs/async/SwingJSUtils.java
new file mode 100644 (file)
index 0000000..82a0265
--- /dev/null
@@ -0,0 +1,651 @@
+package javajs.async;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.stream.Collectors;
+
+import javax.imageio.ImageIO;
+import javax.swing.Timer;
+
+/**
+ * A set of generally useful SwingJS-related methods. Includes:
+ * 
+ * alternatives to using getCodeBase() for loading resources, due to issues in
+ * Eclipse setting that incorrectly (but no problem in JavaScript)
+ * 
+ * 
+ * 
+ * @author hansonr
+ *
+ */
+public class SwingJSUtils {
+       /**
+        * Set the dimension for the applet prior to j2sApplet's call to 
+        * run the applet. Must be used to create a static field:
+        * 
+        * <code>
+        *   private static Dimension dim = 
+        * </code>
+        * 
+        * 
+        * Then, if it is desired also to have Java also set this, add
+        * 
+        *  if (dim != null) setSize(dim);  
+        *  
+        *  to the applet's init() method.
+        * 
+        * @param w
+        * @param h
+        * @return the Dimension
+        * 
+        * @author hansonr
+        */
+       public static Dimension setDim(int w, int h) {
+               String baseURI = (/** @j2sNative document.body.baseURI || */
+               null);
+               boolean isTest = (baseURI == null || baseURI.indexOf("_applet.html") >= 0);
+               if (!isTest)
+                       return null;
+               /**
+                * @j2sNative
+                * 
+                *                      J2S.thisApplet.__Info.width = w; J2S.thisApplet.__Info.height = h;
+                */
+               return new Dimension(w, h);
+       }
+
+       /**
+        * Reliably load a resource of a specific type from the code directory
+        * 
+        * adaptable - here we are returning an image or a string
+        * 
+        * @param cl       the classname of the object to return (Image.class,
+        *                 String.class) null for InputStream
+        * @param filename
+        * @return
+        * 
+        * @author hansonr
+        */
+       public static Object getResource(Class<?> baseClass, String filename, Class<?> cl) {
+               System.out.println("mpUtils.SwingJSUtils.getResource " + baseClass.getCanonicalName() + " " + filename);
+               InputStream is = baseClass.getResourceAsStream(filename);
+               if (cl == Image.class) {
+                       try {
+                               return ImageIO.read(is);
+                       } catch (IOException e) {
+                               e.printStackTrace();
+                       }
+               } else if (cl == String.class) {
+                       return new BufferedReader(new InputStreamReader(is)).lines().collect(Collectors.joining("\n"));
+               }
+               return is;
+       }
+
+       /**
+        * Pre-fetch images during the static entry of the class. This should provide
+        * plenty of clock ticks, since the file transfer is synchronous, and all we are
+        * waiting for is the DOM image object to initialize.
+        * 
+        * @param cl
+        * @param images
+        * @param root
+        * @param nImages
+        * @param ext
+        */
+       public static void loadImagesStatic(Class<?> cl, Image[] images, String root, String ext, int nImages) {
+               for (int i = nImages; --i >= 0;) {
+
+                       // Bild laden und beim MediaTracker registrieren
+                       // MediaTracker ladekontrolle = new MediaTracker(this);
+
+                       // BH SwingJS -- adding generally useful method for loading data
+                       // avoiding the use of getCodeBase(), which for some reason does not work in
+                       // Eclipse.
+
+                       images[i] = (Image) getResource(cl, root + i + "." + ext, Image.class);
+//                     /**
+//                      * @j2sNative
+//                      * $("body").append(images[i]._imgNode);
+//                      * 
+//                      */
+//                       ladekontrolle.addImage(scharf[i],i);
+                       // Warten , bis Bild ganz geladen ist
+
+//                       try {ladekontrolle.waitForID(i);}
+//                       catch (InterruptedException e)
+//                          {}
+               }
+       }
+
+       /**
+        * Fill an array with images based on a String[] listing
+        * @param cl reference class
+        * @param root  optional root path, ending in "/"
+        * @param names source file names
+        * @param images  array to fill
+        */
+       public static void loadImagesStatic(Class<?> cl, String root, String[] names, Image[] images) {
+               for (int i = names.length; --i >= 0;) {
+                       images[i] = (Image) getResource(cl, root + names[i], Image.class);
+               }
+       }
+
+       /**
+        * Eclipse-friendly image getting
+        * 
+        * @param c
+        * @param fileName
+        * @return
+        */
+       public static Image getImage(Component c, String fileName) {
+               return getImage(c.getClass(), fileName);
+       }
+
+       /**
+        * Eclipse-friendly image getting
+        * 
+        * @param c
+        * @param fileName
+        * @return
+        */
+       public static Image getImage(Class<?> c, String fileName) {
+               return (Image) getResource(c, fileName, Image.class);
+       }
+
+       /**
+        * Clear the component graphic. BH added this for JavaScript because changing
+        * the browser zoom can change the size of the canvas for unknown reasons.
+        * 
+        * @param c
+        */
+       public static void clearComponent(Component c) {
+               Graphics gc = c.getGraphics();
+               gc.clearRect(0, 0, c.getWidth(), c.getHeight());
+               gc.dispose();
+       }
+
+       
+       /**
+        * A simple interface to the machine loop, generally of the form 
+        * <code>
+        *   public boolean stateLoop() {
+        *   while (stateHepler.isAlive()) {
+        *     switch (stateHelper.getState()) {
+        *     case STATE_XXX:
+        *        ...
+        *        return stateHelper.delayState(100,STATE_YYY);
+        *     case STATE_YYY:
+        *        ...
+        *        stateHelper.setState(STATE_ZZZ);
+        *        continue;
+        *     case STATE_ZZZ:
+        *        ...
+        *        return stateHelper.delayAction(100, MY_ID, "myCommand", myListener, STATE_XXX);        *   
+        *     case STATE_DONE:
+        *        ...
+        *        stateHelper.interrupt();
+        *        return false;
+        *     }
+        *     return true;
+        *   }
+        *   return false;
+        *   }
+        * </code>
+        * @author hansonr
+        *
+        */
+       public interface StateMachine {
+
+               public boolean stateLoop();
+
+       }
+       /**
+        * StateHelper is a class that facilitates moving from an asychronous multithreaded model to a state-oriented model of programming
+        * for SwingJS animations and other asynchronous business.
+        *  
+        * @author hansonr
+        *
+        */
+       public static class StateHelper {
+               
+               public static final int UNCHANGED = Integer.MIN_VALUE;
+
+               private StateMachine machine;
+               private int state;
+               private int level;
+               
+               private boolean interrupted;
+               
+
+               public StateHelper(StateMachine machine) {
+                       this.machine = machine;
+               }
+
+               public void interrupt() {
+                       interrupted = true;
+               }
+               
+               public boolean isInterrupted() {
+                       return interrupted;
+               }
+               
+               public boolean isAlive() {
+                       return !interrupted;
+               }
+               
+               public void restart() {
+                       interrupted = false;
+               }
+               
+               public void setState(int state) {
+                       this.state = this.stateNext = state;
+               }
+
+               public int getState() {
+                       return state;
+               }
+
+               public void setLevel(int level) {
+                       this.level = this.levelNext = level;
+               }
+
+               public int getLevel() {
+                       return level;
+               }
+               
+               public void setNextState(int next) {
+                       stateNext = next; 
+               }
+               
+               public int getNextState() {
+                       return stateNext;
+               }
+
+               public int getNextLevel() {
+                       return levelNext;
+               }
+
+               public void setNextLevel(int next) {
+                       levelNext = next; 
+               }
+
+               /** 
+                * 
+                * NOTE: this method must remain private; it is accessed via p$1
+                * 
+                * @return
+                */
+               private boolean nextState() {
+                       return next(stateNext, levelNext);
+               }
+               /**
+                * Set the state and run machine.stateLoop().
+                * 
+                * @param state something meaningful to the machine
+                * 
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean next(int state) {
+                       return next(state, 0);
+               }
+               
+               /**
+                * Set the state and level, and then run machine.stateLoop(). Driven directly or via delayedState or delayedAction
+                * 
+                * @param state something meaningful to the machine
+                * @param level something meaningful to the machine
+                * 
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean next(int state, int level) {
+                       return nextStatePriv(this, state, level);
+               }
+
+               private static boolean nextStatePriv(Object oThis, int state, int level) {
+                       StateHelper me = (StateHelper) oThis;
+                       if (me.interrupted)
+                               return false;
+                       if (level != UNCHANGED)
+                               me.level = level;
+                       if (state != UNCHANGED)
+                               me.state = state;
+                       return me.machine.stateLoop();
+               }
+
+               /**
+                * After the given number of milliseseconds, set the new state and run the machines stateLoop with unchanged level
+                * 
+                * @param ms the number of milliseconds to delay; 0 to execute synchronously             * 
+                * @param stateNext  the next state to run
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedState(int ms, int stateNext) {
+                       return delayedState(ms, stateNext, level);
+               }
+
+               private Timer stateTimer;
+
+               private int stateNext;
+               private int levelNext;
+               
+               /**
+                * After the given number of milliseseconds, set the new state and level, and
+                * run the machines stateLoop
+                * 
+                * @param ms        the number of milliseconds to delay; 0 to execute
+                *                  synchronously *
+                * @param stateNext the next state
+                * @param levelNext the next level
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               
+               public boolean delayedState(int ms, int stateNext, int levelNext) {
+                       if (interrupted)
+                               return false;
+                       if (ms == 0)
+                               return next(stateNext, levelNext);
+                       if (stateNext != UNCHANGED)
+                               this.stateNext = stateNext;
+                       if (levelNext != UNCHANGED)
+                               this.levelNext = levelNext;
+                       
+                       /**
+                        * @j2sNative
+                        * var me = this;
+                        * setTimeout(function(){
+                        *  p$1.nextState.apply(me, []);
+                        * },ms);
+                        */
+                       {
+                               // Java only
+
+                               if (stateTimer == null) {
+                                       stateTimer = new Timer(ms, new ActionListener() {
+                                               @Override
+                                               public void actionPerformed(ActionEvent e) {
+                                                       nextState();
+                                               }
+
+                                       });
+                                       stateTimer.setRepeats(false);
+                                       stateTimer.start();
+                               } else {
+                                       stateTimer.restart();
+                               }
+                       }
+                       return true;
+               }
+
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds
+                * 
+                * @param ms       delay milliseconds. if 0, then this action will be called
+                *                 synchronously
+                * @param id       id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                 necessarily
+                * @param command  key for ActionEvent.getCommand()
+                * @param listener ActionListener to be called.
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedAction(int ms, int id, String command, ActionListener listener) {
+                       return delayedAction(ms, id, command, listener, UNCHANGED, UNCHANGED);
+               }
+
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds
+                * 
+                * @param ms       delay milliseconds. if 0, then this action will be called
+                *                 synchronously
+                * @param id       id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                 necessarily
+                * @param command  key for ActionEvent.getCommand()
+                * @param listener ActionListener to be called.
+                * 
+                * @param state    the next state to go to after this listener is called; UNCHANGED to let the listener take care of this.
+                * 
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedAction(int ms, int id, String command, ActionListener listener, int state) {
+                       return delayedAction(ms, id, command, listener, state, UNCHANGED);
+               }               
+               
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds. Setting BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE)
+                * allows the listener to handle continuing the loop.
+                * 
+                * @param ms       delay milliseconds. if 0, then this action will be called
+                *                 synchronously
+                * @param id       id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                 necessarily
+                * @param command  key for ActionEvent.getCommand()
+                * @param listener ActionListener to be called.
+                * @param stateNext  state to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
+                * @param levelNext  level to run after the event is processed by the listener, or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle this.
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedAction(int ms, int id, String command, ActionListener listener, int stateNext, int levelNext) {
+                       if (interrupted)
+                               return false; 
+                       ActionEvent event = new ActionEvent(this, id, command);
+                       if (ms == 0) {
+                               listener.actionPerformed(event);
+                               return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStatePriv(this, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
+                       }
+                       
+                       StateHelper me = this;
+                       
+                       Timer timer = new Timer(ms, id == ActionEvent.ACTION_PERFORMED ? listener : new ActionListener() {
+                               @Override
+                               public void actionPerformed(ActionEvent e) {
+                                       if (!interrupted)
+                                               listener.actionPerformed(event);
+                                       if (!interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
+                                               nextStatePriv(me, stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext);
+                               }
+                               
+                       });
+                       timer.setRepeats(false);
+                       timer.start();
+                       return true;
+               }
+
+               public static void delayedRun(int ms, Runnable runnable) {
+                       new StateHelper(null).delayedRun(ms, runnable, UNCHANGED, UNCHANGED);
+               }
+
+               
+               /**
+                * Fire an actionPerformed event after a given number of milliseconds. Setting
+                * BOTH stateNext and levelNext to UNCHANGED (Integer.MIN_VALUE) allows the
+                * listener to handle continuing the loop.
+                * 
+                * @param ms        delay milliseconds. if 0, then this action will be called
+                *                  synchronously
+                * @param id        id for this event, possibly ACTION_PERFORMED (1001), but not
+                *                  necessarily
+                * @param command   key for ActionEvent.getCommand()
+                * @param listener  ActionListener to be called.
+                * @param stateNext state to run after the event is processed by the listener,
+                *                  or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
+                *                  this.
+                * @param levelNext level to run after the event is processed by the listener,
+                *                  or UNCHANGED (Integer.MIN_VALUE) to allow listener to handle
+                *                  this.
+                * @return not interrupted
+                * 
+                * @author Bob Hanson hansonr@stolaf.edu
+                */
+               public boolean delayedRun(int ms, Runnable runnable, int stateNext, int levelNext) {
+                       if (interrupted)
+                               return false;
+                       if (ms == 0) {
+                               return (stateNext == UNCHANGED && levelNext == UNCHANGED || nextStateIfUnchanged(this, runnable,
+                                               stateNext == UNCHANGED ? state : stateNext, levelNext == UNCHANGED ? level : levelNext));
+                       }
+                       StateHelper me = this;
+                       /**
+                        * @j2sNative
+                        * 
+                        * setTimeout(function() {
+                        * 
+                        *    me.nextStateIfUnchanged$O$O$I$I.apply(me, [me, runnable, stateNext, levelNext]);
+                        * 
+                        * },ms);
+                        */
+                       {
+                               Timer timer = new Timer(ms, new ActionListener() {
+                                       @Override
+                                       public void actionPerformed(ActionEvent e) {
+                                               nextStateIfUnchanged(me, runnable, stateNext, levelNext);
+                                       }
+
+                               });
+                               timer.setRepeats(false);
+                               timer.start();
+                       }
+                       return true;
+               }
+
+               protected boolean nextStateIfUnchanged(Object oThis, Object runnable, int stateNext, int levelNext) {
+                       StateHelper me = (StateHelper)(oThis);
+                       if (!me.interrupted)
+                               ((Runnable) runnable).run();
+                       if (!me.interrupted && (stateNext != UNCHANGED || levelNext != UNCHANGED))
+                               nextStatePriv(oThis, stateNext == UNCHANGED ? me.state : stateNext,
+                                               levelNext == UNCHANGED ? me.level : levelNext);
+                       return true;
+               }
+
+               /**
+                * sleep and then execute the next state
+                * @param ms
+                */
+               public void sleep(int ms) {
+                       int next = stateNext;
+                       delayedState(ms, next);
+               }
+       }
+       
+       /**
+        * open a "url-like" input stream
+        * @param base
+        * @param fileName
+        * @return
+        */
+       public static BufferedInputStream openStream(Class<?> base, String fileName) {
+               String s = (String) getResource(base, fileName, String.class);
+        return new BufferedInputStream(new ByteArrayInputStream(s.getBytes()));
+       }
+
+
+       public static class Performance {
+
+               public final static int TIME_RESET = 0;
+
+               public final static int TIME_MARK = 1;
+
+               public static final int TIME_SET = 2;
+
+               public static final int TIME_GET = 3;
+
+               public static long time, mark, set, duration;
+
+               /**
+                * typical usage:
+                * 
+                * Performance.timeCheck(null, Platform.TIME_MARK);
+                * 
+                * ...
+                * 
+                * Performance.timeCheck("some message", Platform.TIME_MARK);
+                * 
+                * reset...[set/mark]n...get  (total time) (time spent between set and mark)
+                * 
+                * set...get   (total time) (time spent between set and get)
+                * 
+                * long t0 = now(0); ........ ; dt = now(t0); (time since t0)e
+                * 
+                * @param msg
+                * @param mode
+                */
+               public static void timeCheck(String msg, int mode) {
+                       msg = timeCheckStr(msg, mode);
+                       if (msg != null)
+                               System.err.println(msg);
+               }
+
+               public static long now(long t) {
+                       return System.currentTimeMillis() - t;
+               }
+               
+               public static String timeCheckStr(String msg, int mode) {
+                       long t = System.currentTimeMillis();
+                       switch (mode) {
+                       case TIME_RESET:
+                               time = mark = t;
+                               duration = set = 0;
+                               if (msg != null) {
+                                       return ("Platform: timer reset\t\t\t" + msg);
+                               }
+                               break;
+                       case TIME_SET:
+                               if (time == 0)
+                                       time = t;
+                               set = t;
+                               break;
+                       case TIME_MARK:
+                               if (set > 0) {
+                                       // total time between set/mark points
+                                       duration += (t - set);
+                               } else {
+                                       if (time == 0) {
+                                               time = mark = t;
+                                       }
+                                       if (msg != null) {
+                                               long m0 = mark;
+                                               mark = t;
+                                               return ("Platform: timer mark\t" + ((t - time) / 1000f) + "\t" + ((t - m0) / 1000f) + "\t"
+                                                               + msg);
+                                       }
+                                       mark = t;
+                               }
+                               break;
+                       case TIME_GET:
+                               if (msg != null) {
+                                       if (mark < set)
+                                               duration = t - set;
+                                       return ("Platform: timer get\t" + ((t - time) / 1000f) + "\t" + ((duration) / 1000f) + "\t" + msg);
+                               }
+                               set = 0;
+                               break;
+                       }
+                       return null;
+               }
+
+       }
+
+}
\ No newline at end of file
diff --git a/src/swingjs/api/Interface.java b/src/swingjs/api/Interface.java
new file mode 100644 (file)
index 0000000..6616780
--- /dev/null
@@ -0,0 +1,78 @@
+/* $RCSfile$
+ * $Author$
+ * $Date$
+ * $Revision$
+ *
+ * Some portions of this file have been modified by Robert Hanson hansonr.at.stolaf.edu 2012-2017
+ * for use in SwingJS via transpilation into JavaScript using Java2Script.
+ *
+ * Copyright (C) 2006  The Jmol Development Team
+ *
+ * Contact: jmol-developers@lists.sf.net
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+package swingjs.api;
+
+public class Interface {
+       
+       private static String instances=""; 
+
+       public static Object getInstanceWithParams(String name, Class<?>[] classes, Object... params) {
+               try {
+                       Class<?> cl = Class.forName(name);
+                       return  cl.getConstructor(classes).newInstance(params);
+               } catch (Exception e) {
+                       return null;
+               }
+       }
+  public static Object getInstance(String name, boolean isQuiet) {
+       Object x = null;
+       /**
+        * @j2sNative
+        * 
+        * Clazz._isQuietLoad = isQuiet;
+        */
+       {}
+    try {
+       if (!isQuiet && instances.indexOf(name + ";") <= 0) {
+               System.out.println("swingjs.api.Interface creating instance of " + name);
+               instances += name + ";";
+       }
+       Class<?> y = Class.forName(name); 
+      if (y != null)
+       x = y.newInstance();
+    } catch (Throwable e) {
+      System.out.println("Swingjs.api.Interface Error creating instance for " + name + ": \n" + e);
+      /**
+       * @j2sNative
+       * 
+       * if (e.stack)System.out.println(e.stack);
+       */
+      {}
+    } finally {
+       /**
+        * @j2sNative
+        * 
+        * Clazz._isQuietLoad = false;
+        */
+       {}      
+    }
+    return x;          
+  }
+
+}
diff --git a/src/swingjs/api/JSFileHandler.java b/src/swingjs/api/JSFileHandler.java
new file mode 100644 (file)
index 0000000..fde82e8
--- /dev/null
@@ -0,0 +1,7 @@
+package swingjs.api;
+
+public interface JSFileHandler {
+
+       void handleFileLoaded(Object data, String fileName);
+
+}
index 5625f79..322ec83 100644 (file)
@@ -2,184 +2,353 @@ package swingjs.api;
 
 import java.awt.Component;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
+import java.util.function.Function;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.swing.JComponent;
 
 import swingjs.api.js.HTML5Applet;
 
 public interface JSUtilI {
 
-  /**
-   * Indicate to SwingJS that the given file type is binary.
-   * 
-   * @param ext
-   */
-  void addBinaryFileType(String ext);
-
-  /**
-   * Indicate to SwingJS that we can load files using AJAX from the given
-   * domain, such as "www.stolaf.edu", because we know that CORS access has been
-   * provided.
-   * 
-   * @param domain
-   */
-  void addDirectDatabaseCall(String domain);
-
-  /**
-   * Cache or uncache data under the given path name.
-   * 
-   * @param path
-   * @param data
-   *          null to remove from the cache
-   */
-  void cachePathData(String path, Object data);
-
-  /**
-   * Get the HTML5 object corresponding to the specified Component, or the
-   * current thread if null.
-   * 
-   * @param c
-   *          the associated component, or null for the current thread
-   * @return HTML5 applet object
-   */
-  HTML5Applet getAppletForComponent(Component c);
-
-  /**
-   * Get an attribute applet.foo for the applet found using getApplet(null).
-   * 
-   * @param key
-   * @return
-   */
-  Object getAppletAttribute(String key);
-
-  /**
-   * Get the code base (swingjs/j2s, probably) for the applet found using
-   * getApplet(null).
-   * 
-   * @return
-   */
-  URL getCodeBase();
-
-  /**
-   * Get the document base (wherever the page is) for the applet found using
-   * getApplet(null).
-   * 
-   * @return
-   */
-
-  URL getDocumentBase();
-
-  /**
-   * Get an attribute from the div on the page that is associated with this
-   * frame, i.e. with id frame.getName() + "-div".
-   * 
-   * @param frame
-   * @param type
-   *          "node" or "dim"
-   * @return
-   */
-  Object getEmbeddedAttribute(Component frame, String type);
-
-  /**
-   * Get a file synchronously.
-   * 
-   * @param path
-   * @param asString
-   *          true for String; false for byte[]
-   * @return byte[] or String
-   */
-  Object getFile(String path, boolean asString);
-
-  /**
-   * Get the ç§˜bytes field associated with a file, but only if the File object
-   * itself has them attached, not downloading them.
-   * 
-   * @param f
-   * @return
-   */
-  byte[] getBytes(File f);
-
-  /**
-   * Retrieve a HashMap consisting of whatever the application wants, but
-   * guaranteed to be unique to this app context, that is, for the applet found
-   * using getApplet(null).
-   * 
-   * @param contextKey
-   * @return
-   */
-  HashMap<?, ?> getJSContext(Object contextKey);
-
-  /**
-   * Load a resource -- probably a core file -- if and only if a particular
-   * class has not been instantialized. We use a String here because if we used
-   * a .class object, that reference itself would simply load the class, and we
-   * want the core package to include that as well.
-   * 
-   * @param resourcePath
-   * @param className
-   */
-  void loadResourceIfClassUnknown(String resource, String className);
-
-  /**
-   * Read all applet.__Info properties for the applet found using
-   * getApplet(null) that start with the given prefix, such as "jalview_". A
-   * null prefix retrieves all properties. Note that non-string properties will
-   * be stringified.
-   * 
-   * @param prefix
-   *          an application prefix, or null for all properties
-   * @param p
-   *          properties to be appended to
-   */
-  void readInfoProperties(String prefix, Properties p);
-
-  /**
-   * Set an attribute for the applet found using getApplet(null). That is,
-   * applet[key] = val.
-   * 
-   * @param key
-   * @param val
-   */
-  void setAppletAttribute(String key, Object val);
-
-  /**
-   * Set an attribute of applet's Info map for the applet found using
-   * getApplet(null). That is, applet.__Info[key] = val.
-   * 
-   * @param infoKey
-   * @param val
-   */
-  void setAppletInfo(String infoKey, Object val);
-
-  /**
-   * Set the given File object's ç§˜bytes field from an InputStream or a byte[]
-   * array. If the file is a JSTempFile, then also cache those bytes.
-   * 
-   * @param f
-   * @param isOrBytes
-   *          BufferedInputStream, ByteArrayInputStream, FileInputStream, or
-   *          byte[]
-   * @return
-   */
-  boolean setFileBytes(File f, Object isOrBytes);
-
-  /**
-   * Same as setFileBytes, but also caches the data if it is a JSTempFile.
-   * 
-   * @param is
-   * @param outFile
-   * @return
-   */
-  boolean streamToFile(InputStream is, File outFile);
-
-  /**
-   * Switch the flag in SwingJS to use or not use the JavaScript Map object in
-   * Hashtable, HashMap, and HashSet. Default is enabled.
-   * 
-   */
-
-  void setJavaScriptMapObjectEnabled(boolean enabled);
+       /**
+        * The HTML5 canvas delivers [r g b a r g b a ...] which is not a Java option.
+        * The closest Java option is TYPE_4BYTE_ABGR, but that is not quite what we
+        * need. SwingJS decodes TYPE_4BYTE_HTML5 as TYPE_4BYTE_RGBA"
+        * 
+        * ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+        * 
+        * int[] nBits = { 8, 8, 8, 8 };
+        * 
+        * int[] bOffs = { 0, 1, 2, 3 };
+        * 
+        * colorModel = new ComponentColorModel(cs, nBits, true, false,
+        * Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
+        * 
+        * raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, height,
+        * width * 4, 4, bOffs, null);
+        * 
+        * Note, however, that this buffer type should only be used for direct buffer access
+        * using
+        * 
+        * 
+        * 
+        */
+       public static final int TYPE_4BYTE_HTML5 = -6;
+       
+       /**
+        * The HTML5 VIDEO element wrapped in a BufferedImage. 
+        * 
+        * To be extended to allow video capture?
+        */
+       public static final int TYPE_HTML5_VIDEO = Integer.MIN_VALUE;
+
+       /**
+        * Indicate to SwingJS that the given file type is binary.
+        * 
+        * @param ext
+        */
+       void addBinaryFileType(String ext);
+
+       /**
+        * Indicate to SwingJS that we can load files using AJAX from the given domain,
+        * such as "www.stolaf.edu", because we know that CORS access has been provided.
+        * 
+        * @param domain
+        */
+       void addDirectDatabaseCall(String domain);
+
+       /**
+        * Cache or uncache data under the given path name.
+        * 
+        * @param path
+        * @param data null to remove from the cache
+        */
+       void cachePathData(String path, Object data);
+
+       /**
+        * Get the HTML5 object corresponding to the specified Component, or the current thread if null.
+        * 
+        * @param c  the associated component, or null for the current thread
+        * @return HTML5 applet object
+        */
+       HTML5Applet getAppletForComponent(Component c);
+
+       /**
+        * Get an attribute applet.foo for the applet found using getApplet(null).
+        * 
+        * @param key
+        * @return
+        */
+       Object getAppletAttribute(String key);
+
+
+       /**
+        * Get the applet's __Info map or an attribute of that map for the applet found using
+        * getApplet(null). That is, applet.__Info or applet.__Info[InfoKey].
+        * 
+        * @param infoKey if null, return the full __Info map
+        */
+       Object getAppletInfo(String infoKey);
+
+       /**
+        * Get the code base (swingjs/j2s, probably) for the applet found using
+        * getApplet(null).
+        * 
+        * @return
+        */
+       URL getCodeBase();
+
+       /**
+        * Get the document base (wherever the page is) for the applet found using
+        * getApplet(null).
+        * 
+        * @return
+        */
+
+       URL getDocumentBase();
+
+       /**
+        * Get an attribute from the div on the page that is associated with this frame,
+        * i.e. with id frame.getName() + "-div".
+        * 
+        * @param frame
+        * @param type  "node" or "dim"
+        * @return
+        */
+       Object getEmbeddedAttribute(Component frame, String type);
+
+       /**
+        * Get a file synchronously.
+        * 
+        * @param path
+        * @param asString true for String; false for byte[]
+        * @return byte[] or String
+        */
+       Object getFile(String path, boolean asString);
+
+       /**
+        * Get the ç§˜bytes field associated with a file, but only if the File object itself has
+        * them attached, not downloading them.
+        * 
+        * @param f
+        * @return
+        */
+       byte[] getBytes(File f);
+
+       /**
+        * Retrieve a HashMap consisting of whatever the application wants, but
+        * guaranteed to be unique to this app context, that is, for the applet found using
+        * getApplet(null).
+        * 
+        * @param contextKey
+        * @return
+        */
+       HashMap<?, ?> getJSContext(Object contextKey);
+
+       /**
+        * Load a resource -- probably a core file -- if and only if a particular class
+        * has not been instantialized. We use a String here because if we used a .class
+        * object, that reference itself would simply load the class, and we want the
+        * core package to include that as well.
+        * 
+        * @param resourcePath
+        * @param className
+        */
+       void loadResourceIfClassUnknown(String resource, String className);
+
+       /**
+        * Read all applet.__Info properties  for the applet found using
+        * getApplet(null) that start with the given prefix, such as "jalview_".
+        * A null prefix retrieves all properties. Note that non-string properties will be
+        * stringified.
+        * 
+        * @param prefix an application prefix, or null for all properties
+        * @param p      properties to be appended to
+        */
+       void readInfoProperties(String prefix, Properties p);
+
+       /**
+        * Set an attribute for the applet found using
+        * getApplet(null). That is, applet[key] = val.
+        * 
+        * @param key
+        * @param val
+        */
+       void setAppletAttribute(String key, Object val);
+
+       /**
+        * Set an attribute of applet's Info map for the applet found using
+        * getApplet(null). That is, applet.__Info[key] = val.
+        * 
+        * @param infoKey
+        * @param val
+        */
+       void setAppletInfo(String infoKey, Object val);
+
+       /**
+        * Set the given File object's ç§˜bytes field from an InputStream or a byte[] array.
+        * If the file is a JSTempFile, then also cache those bytes.
+        * 
+        * @param f
+        * @param isOrBytes BufferedInputStream, ByteArrayInputStream, FileInputStream, or byte[]
+        * @return
+        */
+       boolean setFileBytes(File f, Object isOrBytes);
+
+       /**
+        * Set the given URL object's _streamData field from an InputStream or a byte[] array.
+        * 
+        * @param f
+        * @param isOrBytes BufferedInputStream, ByteArrayInputStream, FileInputStream, or byte[]
+        * @return
+        */
+       boolean setURLBytes(URL url, Object isOrBytes);
+
+       /**
+        * Same as setFileBytes.
+        * 
+        * @param is
+        * @param outFile
+        * @return
+        */
+       boolean streamToFile(InputStream is, File outFile);
+
+         /**
+          * Switch the flag in SwingJS to use or not use the JavaScript Map object in
+          * Hashtable, HashMap, and HashSet. Default is enabled.
+          *       * 
+          */
+       void setJavaScriptMapObjectEnabled(boolean enabled);
+
+
+       /**
+        * Open a URL in a browser tab.
+        * 
+        * @param url
+        * @param target null or specific tab, such as "_blank"
+        */
+       void displayURL(String url, String target);
+
+       /**
+        * Retrieve cached bytes for a path (with unnormalized name)
+        * from J2S._javaFileCache.
+        * 
+        * @param path
+        * 
+        * @return byte[] or null
+        */
+       byte[] getCachedBytes(String path);
+       
+       /**
+        * Attach cached bytes to a file-like object, including URL,
+        * or anything having a ç§˜bytes field (File, URI, Path)
+        * from J2S._javaFileCache. That is, allow two such objects
+        * to share the same underlying byte[ ] array.
+        * 
+        * 
+        * @param URLorURIorFile
+        * @return byte[] or null
+        */
+       byte[] addJSCachedBytes(Object URLorURIorFile);
+
+       /**
+        * Seek an open ZipInputStream to the supplied ZipEntry, if possible.
+        * 
+        * @param zis the ZipInputStream
+        * @param ze  the ZipEntry
+        * @return the length of this entry, or -1 if, for whatever reason, this was not possible
+        */
+       long seekZipEntry(ZipInputStream zis, ZipEntry ze);
+
+       /**
+        * Retrieve the byte array associated with a ZipEntry.
+        * 
+        * @param ze
+        * @return
+        */
+       byte[] getZipBytes(ZipEntry ze);
+
+       /**
+        * Java 9 method to read all (remaining) bytes from an InputStream. In SwingJS,
+        * this may just create a new reference to an underlying Int8Array without
+        * copying it.
+        * 
+        * @param zis
+        * @return
+        * @throws IOException 
+        */
+       byte[] readAllBytes(InputStream zis) throws IOException;
+
+       /**
+        * Java 9 method to transfer all (remaining) bytes from an InputStream to an OutputStream.
+        * 
+        * @param is
+        * @param out
+        * @return
+        * @throws IOException
+        */
+       long transferTo(InputStream is, OutputStream out) throws IOException;
+
+       /**
+        * Retrieve any bytes already attached to this URL.
+        * 
+        * @param url
+        * @return
+        */
+       byte[] getURLBytes(URL url);
+
+       /**
+        * Set a message in the lower-left-hand corner SwingJS status block.
+        * 
+        * @param msg
+        * @param doFadeOut
+        */
+       void showStatus(String msg, boolean doFadeOut);
+
+       /**
+        * Asynchronously retrieve the byte[] for a URL.
+        * 
+        * @param url
+        * @param whenDone
+        */
+       void getURLBytesAsync(URL url, Function<byte[], Void> whenDone);
+
+       /**
+        * Experimental method to completely disable a Swing Component's user interface.
+        * 
+        * @param jc
+        * @param enabled
+        */
+       void setUIEnabled(JComponent jc, boolean enabled);
+
+
+       /**
+        * Play an audio
+        * @param buffer
+        * @param format a javax.sound.sampled.AudioFormat
+        * @throws Exception 
+        */
+       void playAudio(byte[] buffer, Object format) throws Exception;
+
+       /**
+        * For either an applet or an application, get the ORIGINAL __Info as a Map that
+        * has a full set up lower-case keys along with whatever non-all-lower-case keys
+        * provided at start-up.
+        * 
+        * @return
+        */
+       Map<String, Object> getAppletInfoAsMap();
+
+       
+  void setAppClass(Object j);
 
 }
diff --git a/src/swingjs/api/js/DOMNode.java b/src/swingjs/api/js/DOMNode.java
new file mode 100644 (file)
index 0000000..4de6ee0
--- /dev/null
@@ -0,0 +1,309 @@
+package swingjs.api.js;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+/**
+ * A mix of direct DOM calls on DOM nodes and convenience methods to do that.
+ * 
+ * NOTE: DO NOT OVERLOAD THESE METHODS, as this package will not be qualified.
+ * 
+ * @author hansonr
+ *
+ */
+public interface DOMNode {
+
+       public static JQuery jQuery = /** @j2sNative jQuery.$ || (jQuery.$ = jQuery) || */null;
+
+       // "abstract" in the sense that these are the exact calls to JavaScript
+       
+
+       public void addEventListener(String event, Object listener);
+       public void removeEventListener(String event);
+       public void removeEventListener(String event, Object listener);
+
+
+
+       public String[] getAttributeNames();
+
+       public String getAttribute(String name);
+
+       public void setAttribute(String attr, String val);
+
+       public void appendChild(DOMNode node);
+       
+       public void prepend(DOMNode node);
+       
+       public void insertBefore(DOMNode node, DOMNode refNode);
+       
+       public DOMNode removeChild(DOMNode node);
+
+       public void focus();
+       public boolean hasFocus();
+       public void blur();
+
+       public DOMNode removeAttribute(String attr);
+       
+       public void setSelectionRange(int start, int end, String direction);
+
+       public Rectangle getBoundingClientRect();
+       
+       // static convenience methods
+
+       public static DOMNode createElement(String key, String id) {
+               DOMNode node = null;
+               /**
+                * @j2sNative
+                *                                      node = document.createElement(key);
+                *                                      id && (node.id = id);
+                */
+               return node;
+       }
+
+       public static DOMNode getElement(String id) {
+               return (/**  @j2sNative  document.getElementById(id) ||*/ null);
+       }
+
+       public static DOMNode createTextNode(String text) {
+               return (/** @j2sNative document.createTextNode(text) || */ null); 
+       }
+
+       public static DOMNode getParent(DOMNode node) {
+               return (/**  @j2sNative  node.parentNode ||*/ null);
+       }
+       
+       public static DOMNode getPreviousSibling(DOMNode node) {
+               return (/**  @j2sNative  node.previousSibling ||*/ null);
+       }
+       
+       public static DOMNode firstChild(DOMNode node) {
+               return  (/**  @j2sNative node.firstChild ||*/ null);
+       }
+
+       public static DOMNode lastChild(DOMNode node) {
+               return  (/**  @j2sNative node.lastChild ||*/ null);
+       }
+
+       public static DOMNode setZ(DOMNode node, int z) {
+               return setStyles(node, "z-index", "" + z);
+       }
+
+       public static Object getAttr(Object node, String attr) {
+               /**
+                * @j2sNative
+                * 
+                * if (!node)
+                *   return null;
+                * var a = node[attr];
+                * return (typeof a == "undefined" ? null : a); 
+                */
+               {
+               return null;
+               }
+       }
+
+       public static int getAttrInt(DOMNode node, String attr) {
+               return  (/**  @j2sNative node && node[attr] ||*/ 0);
+       }
+
+       public static String getStyle(DOMNode node, String style) {
+               return  (/**  @j2sNative node && node.style[style] ||*/ null);
+       }
+
+       public static void getCSSRectangle(DOMNode node, Rectangle r) {
+               /**
+                * @j2sNative
+                * 
+                *       r.x = parseInt(node.style.left.split("p")[0]);
+                *       r.y = parseInt(node.style.top.split("p")[0]);
+                *       r.width = parseInt(node.style.width.split("p")[0]);
+                *       r.height = parseInt(node.style.height.split("p")[0]);
+                * 
+                */
+       }
+
+       public static DOMNode setAttr(DOMNode node, String attr, Object val) {
+               /**
+                * @j2sNative
+                * 
+                *                      attr && (node[attr] = (val == "秘TRUE" ? true : val == "秘FALSE" ? false : val));
+                * 
+                */
+               return node;
+       }
+
+
+       public static void setAttrInt(DOMNode node, String attr, int val) {
+               /**
+                * @j2sNative
+                * 
+                *                      node[attr] = val;
+                * 
+                */
+       }
+
+
+       /**
+        * allows for null key to be skipped (used in audio)
+        * 
+        * @param node
+        * @param attr
+        * @return
+        */
+       public static DOMNode setAttrs(DOMNode node, Object... attr) {
+               /**
+                * @j2sNative
+                * 
+                *            for (var i = 0; i < attr.length;) { 
+                *              C$.setAttr(node, attr[i++],attr[i++]);
+                *            }
+                */
+               return node;
+       }
+
+       public static DOMNode setStyles(DOMNode node, String... attr) {
+               /**
+                * @j2sNative
+                * 
+                *            if (node) for (var i = 0; i < attr.length;) {
+                *             node.style[attr[i++]] = attr[i++];
+                *             }
+                * 
+                */
+               return node;
+       }
+
+       public static DOMNode setSize(DOMNode node, int width, int height) {
+               return setStyles(node, "width", width + "px", "height", height + "px");
+       }
+
+       public static DOMNode setPositionAbsolute(DOMNode node) {
+               return DOMNode.setStyles(node, "position", "absolute");
+       }
+
+       public static void setVisible(DOMNode node, boolean visible) {
+               setStyles(node, "display", visible ? "block" : "none");
+       }
+
+       public static DOMNode setTopLeftAbsolute(DOMNode node, int top, int left) {
+               DOMNode.setStyles(node, "top", top + "px");
+               DOMNode.setStyles(node, "left", left + "px");
+               return DOMNode.setStyles(node, "position", "absolute");
+       }
+
+       public static void addHorizontalGap(DOMNode domNode, int gap) {
+               DOMNode label = DOMNode.setStyles(DOMNode.createElement("label", null), 
+                               "letter-spacing", gap + "px", "font-size", "0pt");
+               label.appendChild(DOMNode.createTextNode("."));
+               domNode.appendChild(label);
+       }
+
+       public static void appendChildSafely(DOMNode parent, DOMNode node) {
+               /**
+                * @j2sNative
+                * if (!parent || node.parentElement == parent)
+                *   return;
+                */
+               parent.appendChild(node);
+       }
+       
+       // static jQuery calls
+       
+       /**
+        * jQuery height()
+        * 
+        * @param node
+        * @return height
+        */
+       public static int getHeight(DOMNode node) {
+               return jQuery.$(node).height();
+       }
+
+       /**
+        * jQuery width()
+        * 
+        * @param node
+        * @return width
+        */
+       public static int getWidth(DOMNode node) {
+               return jQuery.$(node).width();
+       }
+
+       /**
+        * jQuery remove()
+        * 
+        * Remove this node and return its parent. Automatically removing all events
+        * attached to it.
+        * 
+        * @param node
+        * @return parent or null
+        */
+       public static void dispose(DOMNode node) {
+               if (node != null)               
+                       jQuery.$(node).remove();
+       }
+
+       /**
+        * Just remove the node, keeping its events and data 
+        * @param node
+        */
+       public static void remove(DOMNode node) {
+               
+               // NOTE: IE does not have node.remove()
+               
+               DOMNode p = getParent(node);
+               if (p != null)
+                       p.removeChild(node);
+       }
+
+       /**
+        * just detaches all the nodes; doesn't remove their listeners
+        * @param node
+        */
+       public static void detachAll(DOMNode node) {
+               /**
+                * @j2sNative
+                *  if(node)
+                *    while(node.lastChild)
+                *      node.removeChild(node.lastChild);
+                */
+       }
+       
+       /**
+        * jQuery detach() + append()
+        * 
+        * @param node
+        * @param container
+        * @return parent if container is null, or container if it is not null
+        */
+       public static DOMNode transferTo(DOMNode node, DOMNode container) {
+               if (node == null)
+                       return null;
+               DOMNode p = getParent(node);
+               try {
+                       if (p != null)
+                               jQuery.$(node).detach();
+               } catch (Throwable e) {
+                       // ignore
+               }
+                if (container == null)
+                       return p; 
+                jQuery.$(container).append(node);
+               return container;
+       }
+
+       public static Object getEmbedded(String name, String type) {
+               DOMNode node = DOMNode.getElement(name + "-div");
+               if (node == null)
+                       return null;
+               switch (type) {
+               case "node":
+                       return node;
+               case "dim":
+                       return new Dimension(DOMNode.getWidth(node), DOMNode.getHeight(node));
+               default:
+                       return DOMNode.getAttr(node, type);
+               }
+       }
+
+}
diff --git a/src/swingjs/api/js/HTML5AudioContext.java b/src/swingjs/api/js/HTML5AudioContext.java
new file mode 100644 (file)
index 0000000..fecc66b
--- /dev/null
@@ -0,0 +1,58 @@
+package swingjs.api.js;
+
+public interface HTML5AudioContext {
+
+       // https://developer.mozilla.org/en-US/docs/Web/API/AudioContext
+       // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API
+       
+       void close();
+
+       float[] createBuffer(int nChannels, int frameCount, int sampleRate);
+
+       void createBufferSource();
+
+       void createMediaElementSource();
+
+       void createMediaStreamSource();
+
+       void createMediaStreamDestination();
+
+       void createScriptProcessor();
+
+       //void createStereoPanner();
+
+       void createAnalyser();
+
+       void createBiquadFilter();
+
+       void createChannelMerger();
+
+       void createChannelSplitter();
+
+       void createConvolver();
+
+       void createDelay();
+
+       void createDynamicsCompressor();
+
+       void createGain();
+
+       void createIIRFilter();
+
+       void createOscillator();
+
+       void createPanner();
+
+       void createPeriodicWave();
+
+       void createWaveShaper();
+
+       void createAudioWorker();
+
+       void decodeAudioData();
+
+       void resume();
+
+       void suspend();
+
+}
diff --git a/src/swingjs/api/js/HTML5Canvas.java b/src/swingjs/api/js/HTML5Canvas.java
new file mode 100644 (file)
index 0000000..f6d0604
--- /dev/null
@@ -0,0 +1,59 @@
+package swingjs.api.js;
+
+import java.awt.image.BufferedImage;
+
+public interface HTML5Canvas extends DOMNode {
+
+       HTML5CanvasContext2D getContext(String str2d);
+
+       /*
+        * Retrieves the byte[] data buffer from an HTML5 CANVAS element, optionally
+        * first setting its contents to a source IMG, CANVAS, or VIDEO element.
+        * 
+        */
+       static byte[] getDataBufferBytes(HTML5Canvas canvas, DOMNode sourceNode, int w, int h) {
+               if (sourceNode != null) {
+                       DOMNode.setAttrInt(canvas, "width", w);
+                       DOMNode.setAttrInt(canvas, "height", h);
+               }
+               HTML5CanvasContext2D ctx = canvas.getContext("2d");
+               if (sourceNode != null) {
+                       ctx.drawImage(sourceNode, 0, 0, w, h);
+               }
+               // Coerse int[] to byte[]
+               return (byte[]) (Object) ctx.getImageData(0, 0, w, h).data;
+       }
+
+       /**
+        * Install a source image (img, video, or canvas) into a matching BufferedImage 
+        * 
+        * @param sourceNode
+        * @param image
+        */
+       static void setImageNode(DOMNode sourceNode, BufferedImage image) {
+               /**
+                * @j2sNative
+                * 
+                *                      image._setImageNode$O$Z(sourceNode, false);
+                * 
+                */             {
+                       // image._setImageNode(sourceNode, false);
+                }
+       }
+       
+       
+
+       static HTML5Canvas createCanvas(int width, int height, String id) {
+               HTML5Canvas canvas = (HTML5Canvas) DOMNode.createElement("canvas", (id == null ? "img" + Math.random() : id + ""));
+               DOMNode.setStyles(canvas, "width", width + "px", "height", height + "px");
+               /**
+                * @j2sNative
+                * 
+                * canvas.width = width;
+                * canvas.height = height;
+                * 
+                */
+               return canvas;
+       }
+
+}
diff --git a/src/swingjs/api/js/HTML5CanvasContext2D.java b/src/swingjs/api/js/HTML5CanvasContext2D.java
new file mode 100644 (file)
index 0000000..226f270
--- /dev/null
@@ -0,0 +1,164 @@
+package swingjs.api.js;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D.Float;
+
+public abstract class HTML5CanvasContext2D {
+
+       public class ImageData {
+               public int[] data; 
+       }
+
+       public ImageData imageData;
+       
+       public Object[][] _aSaved;
+       
+       public double lineWidth;
+
+       public String font, fillStyle, strokeStyle;
+
+       public float globalAlpha;
+
+       public abstract void drawImage(DOMNode img, double sx,
+                       double sy, double swidth, double sheight, double dx, double dy, double width, double height);
+
+       public abstract ImageData getImageData(int x, int y, int width, int height);
+
+       public abstract void beginPath();
+
+       public abstract void moveTo(double x0, double y0);
+
+       public abstract void lineTo(double x1, double y1);
+
+       public abstract void stroke();
+
+       public abstract void save();
+
+       public abstract void scale(double f, double g);
+
+       public abstract void arc(double centerX, double centerY, double radius, double startAngle, double  endAngle, boolean counterclockwise);
+
+       public abstract void closePath();
+
+       public abstract void restore();
+
+       public abstract void translate(double x, double y);
+       
+       public abstract void rotate(double radians);
+
+       public abstract void fill();
+
+
+       public abstract void fill(String winding);
+
+       public abstract void rect(double x, double y, double width, double height);
+
+       public abstract void fillText(String s, double x, double y);
+
+       public abstract void fillRect(double x, double y, double width, double height);
+
+       public abstract void clearRect(double i, double j, double windowWidth, double windowHeight);
+
+       public abstract void setLineDash(int[] dash);
+
+       public abstract void clip();
+
+       public abstract void quadraticCurveTo(double d, double e, double f, double g);
+
+       public abstract void bezierCurveTo(double d, double e, double f, double g, double h, double i);
+
+       public abstract void drawImage(DOMNode img, double x, double y, double width, double height);
+
+       public abstract void putImageData(Object imageData, double x, double y);
+
+       public abstract void transform(double d, double shx, double e, double shy, double f, double g);
+
+
+       /**
+        * pull one save structure onto the stack array ctx._aSaved
+        * 
+        * @param ctx
+        * @return the length of the stack array after the push
+        */
+       public static int push(HTML5CanvasContext2D ctx, Object[] map) {
+               /**
+                * @j2sNative
+                * 
+                * (ctx._aSaved || (ctx._aSaved = [])).push(map); 
+                * return ctx._aSaved.length;
+                */
+               {
+                       return 0;
+               }
+       }
+
+       /**
+        * pull one save structure off the stack array ctx._aSaved
+        * 
+        * @param ctx
+        * @return
+        */
+       public static Object[] pop(HTML5CanvasContext2D ctx) {
+               /**
+                * @j2sNative
+                * 
+                * return (ctx._aSaved && ctx._aSaved.length > 0 ? ctx._aSaved.pop() : null); 
+                */
+               {
+                       return null;
+               }
+       }
+
+       public static int getSavedLevel(HTML5CanvasContext2D ctx) {
+               /**
+                * @j2sNative
+                * 
+                * return (ctx._aSaved ? ctx._aSaved.length : 0); 
+                */
+               {
+                       return 0;
+               }
+       }
+       
+       public static Object[][] getSavedStack(HTML5CanvasContext2D ctx) {
+          /**
+           * @j2sNative
+           * 
+           * return (ctx._aSaved || []);
+           */
+               {
+                       return null;
+               }
+               
+       }
+
+       public static double[] setMatrix(HTML5CanvasContext2D ctx, AffineTransform transform) {
+               double[] m = /**  @j2sNative ctx._m || */ null;
+               if (transform == null) {
+                       /** @j2sNative ctx._m = null; */
+                       return null;                    
+               }
+               if (m == null) {
+                       /**
+                        * @j2sNative
+                        * ctx._m = m = new Array(6);
+                        */
+                       transform.getMatrix(m);
+               }
+               return m;
+       }
+
+       public static void createLinearGradient(HTML5CanvasContext2D ctx, Float p1, Float p2, String css1, String css2) {
+               /**
+                * @j2sNative
+                * 
+                *   var grd = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y);
+                *   grd.addColorStop(0,css1);
+                *   grd.addColorStop(1,css2);
+                *   ctx.fillStyle = grd;
+                */
+               }
+
+       abstract public void drawImage(DOMNode domNode, int x, int y);
+
+}
diff --git a/src/swingjs/api/js/HTML5DataTransfer.java b/src/swingjs/api/js/HTML5DataTransfer.java
new file mode 100644 (file)
index 0000000..b6d91ba
--- /dev/null
@@ -0,0 +1,7 @@
+package swingjs.api.js;
+
+public interface HTML5DataTransfer {
+
+       Object getData(String type);
+
+}
diff --git a/src/swingjs/api/js/HTML5Video.java b/src/swingjs/api/js/HTML5Video.java
new file mode 100644 (file)
index 0000000..073f1ed
--- /dev/null
@@ -0,0 +1,472 @@
+package swingjs.api.js;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.net.URL;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.function.Function;
+
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import swingjs.api.JSUtilI;
+
+/**
+ * A full-service interface for HTML5 video element interaction. Allows setting
+ * and getting HTML5 video element properties. ActionListeners can be set to
+ * listen for JavaScript events associated with a video element.
+ * 
+ * Video is added using a JavaScript-only two-parameter constructor for
+ * ImageIcon with "jsvideo" as the description, allowing for video construction
+ * from byte[], File, or URL.
+ * 
+ * After adding the ImageIcon to a JLabel, calling
+ * jlabel.getClientProperty("jsvideo") returns an HTML5 object of type
+ * HTML5Video (the &lt;video&gt; tag), which has the full suite of HTML5 video
+ * element properties, methods, and events.
+ * 
+ * Access to event listeners is via the method addActionListener, below, which
+ * return an ActionEvent that has as its source both the video element source as
+ * well as the original JavaScript event as an Object[] { jsvideo, event }. The
+ * id of this ActionEvent is 12345, and its command is the name of the event,
+ * for example, "canplay" or "canplaythrough".
+ * 
+ * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement for
+ * details.
+ * 
+ * @author hansonr
+ *
+ */
+public interface HTML5Video extends DOMNode {
+
+       public interface Promise {
+
+       }
+
+       final static String[] eventTypes = new String[] { "audioprocess", // The input buffer of a ScriptProcessorNode is
+                                                                                                                                               // ready to be processed.
+                       "canplay", // The browser can play the media, but estimates that not enough data has been
+                                               // loaded to play the media up to its end without having to stop for further
+                                               // buffering of content.
+                       "canplaythrough", // The browser estimates it can play the media up to its end without stopping
+                                                               // for content buffering.
+                       "complete", // The rendering of an OfflineAudioContext is terminated.
+                       "durationchange", // The duration attribute has been updated.
+                       "emptied", // The media has become empty; for example, this event is sent if the media has
+                                               // already been loaded (or partially loaded), and the load() method is called to
+                                               // reload it.
+                       "ended", // Playback has stopped because the end of the media was reached.
+                       "loadeddata", // The first frame of the media has finished loading.
+                       "loadedmetadata", // The metadata has been loaded.
+                       "pause", // Playback has been paused.
+                       "play", // Playback has begun.
+                       "playing", // Playback is ready to start after having been paused or delayed due to lack of
+                                               // data.
+                       "progress", // Fired periodically as the browser loads a resource.
+                       "ratechange", // The playback rate has changed.
+                       "seeked", // A seek operation completed.
+                       "seeking", // A seek operation began.
+                       "stalled", // The user agent is trying to fetch media data, but data is unexpectedly not
+                                               // forthcoming.
+                       "suspend", // Media data loading has been suspended.
+                       "timeupdate", // The time indicated by the currentTimeattribute has been updated.
+                       "volumechange", // The volume has changed.
+                       "waiting", // Playback has stopped because of a temporary lack of data
+       };
+
+       // direct methods
+
+       public void addTextTrack() throws Throwable;
+
+       public Object captureStream() throws Throwable;
+
+       public String canPlayType(String mediaType) throws Throwable;
+
+       public void fastSeek(double time) throws Throwable;
+
+       public void load() throws Throwable;
+
+       public void mozCaptureStream() throws Throwable;
+
+       public void mozCaptureStreamUntilEnded() throws Throwable;
+
+       public void mozGetMetadata() throws Throwable;
+
+       public void pause() throws Throwable;
+
+       public Promise play() throws Throwable;
+
+       public Promise seekToNextFrame() throws Throwable;
+
+       public Promise setMediaKeys(Object mediaKeys) throws Throwable;
+
+       public Promise setSinkId(String id) throws Throwable;
+
+       // convenience methods
+
+       public static double getDuration(HTML5Video v) {
+               return /** @j2sNative v.duration || */
+               0;
+       }
+
+       public static double setCurrentTime(HTML5Video v, double time) {
+               return /** @j2sNative v.currentTime = time|| */
+               0;
+       }
+
+       public static double getCurrentTime(HTML5Video v) {
+               return /** @j2sNative v.currentTime|| */
+               0;
+       }
+
+       public static Dimension getSize(HTML5Video v) {
+               return new Dimension(/** @j2sNative v.videoWidth || */
+                               0, /** @j2sNative v.videoHeight|| */
+                               0);
+       }
+
+       /**
+        * 
+        * Create a BufferedIfmage from the current frame. The image will be of type
+        * swingjs.api.JSUtilI.TYPE_4BYTE_HTML5, matching the data buffer of HTML5
+        * images.
+        * 
+        * @param v
+        * @param imageType  if Integer.MIN_VALUE, swingjs.api.JSUtilI.TYPE_4BYTE_HTML5
+        * @return
+        */
+       public static BufferedImage getImage(HTML5Video v, int imageType) {
+               Dimension d = HTML5Video.getSize(v);
+               BufferedImage image = (BufferedImage) HTML5Video.getProperty(v, "_image");
+               if (image == null || image.getWidth() != d.width || image.getHeight() != d.height) {
+                       image = new BufferedImage(d.width, d.height, imageType == Integer.MIN_VALUE ? JSUtilI.TYPE_4BYTE_HTML5 : imageType);
+                       HTML5Video.setProperty(v, "_image", image);
+               }
+               HTML5Canvas.setImageNode(v, image);
+               return image;
+       }
+
+       // property setting and getting
+
+       /**
+        * Set a property of the the HTML5 video element using jsvideo[key] = value.
+        * Numbers and Booleans will be unboxed.
+        * 
+        * @param jsvideo the HTML5 video element
+        * @param key
+        * @param value
+        */
+       public static void setProperty(HTML5Video jsvideo, String key, Object value) {
+               if (value instanceof Number) {
+                       /** @j2sNative jsvideo[key] = +value; */
+               } else if (value instanceof Boolean) {
+                       /** @j2sNative jsvideo[key] = !!+value */
+               } else {
+                       /** @j2sNative jsvideo[key] = value; */
+               }
+       }
+
+       /**
+        * Get a property using jsvideo[key], boxing number as Double and boolean as
+        * Boolean.
+        * 
+        * @param jsvideo the HTML5 video element
+        * 
+        * @param key
+        * @return value or value boxed as Double or Boolean
+        */
+       @SuppressWarnings("unused")
+       public static Object getProperty(HTML5Video jsvideo, String key) {
+               Object val = (/** @j2sNative 1? jsvideo[key] : */
+               null);
+               if (val == null)
+                       return null;
+               switch (/** @j2sNative typeof val || */
+               "") {
+               case "number":
+                       return Double.valueOf(/** @j2sNative val || */
+                                       0);
+               case "boolean":
+                       return Boolean.valueOf(/** @j2sNative val || */
+                                       false);
+               default:
+                       return val;
+               }
+       }
+
+       // event action
+
+       /**
+        * Add an ActionListener for the designated events. When an event is fired,
+        * 
+        * @param jsvideo  the HTML5 video element
+        * @param listener
+        * @param events   array of events to listen to or null to listen on all video
+        *                 element event types
+        * @return an array of event/listener pairs that can be used for removal.
+        */
+       public static Object[] addActionListener(HTML5Video jsvideo, ActionListener listener, String... events) {
+               if (events == null || events.length == 0)
+                       events = eventTypes;
+               @SuppressWarnings("unused")
+               Function<Object, Void> f = new Function<Object, Void>() {
+
+                       @Override
+                       public Void apply(Object jsevent) {
+                               String name = (/** @j2sNative jsevent.type || */
+                               "?");
+                               System.out.println("HTML5Video " + name);
+                               ActionEvent e = new ActionEvent(new Object[] { jsvideo, jsevent }, 12345, name,
+                                               System.currentTimeMillis(), 0);
+                               listener.actionPerformed(e);
+                               return null;
+                       }
+               };
+               ArrayList<Object> listeners = new ArrayList<>();
+               for (int i = 0; i < events.length; i++) {
+                       Object func = /**
+                                                        * @j2sNative function(event){f.apply$O.apply(f, [event])} ||
+                                                        */
+                                       null;
+                       listeners.add(events[i]);
+                       listeners.add(func);
+                       if (jsvideo != null)
+                               jsvideo.addEventListener(events[i], func);
+
+               }
+               return listeners.toArray(new Object[listeners.size()]);
+       }
+
+       /**
+        * Remove action listener
+        * 
+        * @param jsvideo   the HTML5 video element
+        * @param listeners an array of event/listener pairs created by
+        *                  addActionListener
+        */
+       public static void removeActionListener(HTML5Video jsvideo, Object[] listeners) {
+               if (listeners == null) {
+                       for (int i = 0; i < eventTypes.length; i++) {
+                               jsvideo.removeEventListener(eventTypes[i]);
+                       }
+               }
+               
+               for (int i = 0; i < listeners.length; i += 2) {
+                       String event = (String) listeners[i];
+                       Object listener = listeners[i + 1];
+                       jsvideo.removeEventListener(event, listener);
+               }
+       }
+
+       /**
+        * Create an ImageIcon which, when placed in a JLabel, displays the video.
+        * 
+        * @param source
+        * @return
+        */
+       public static ImageIcon createIcon(Object source) {
+               try {
+                       if (source instanceof URL) {
+                               return new ImageIcon((URL) source, "jsvideo");
+                       } else if (source instanceof byte[]) {
+                               return new ImageIcon((byte[]) source, "jsvideo");
+                       } else if (source instanceof File) {
+                               return new ImageIcon(Files.readAllBytes(((File) source).toPath()));
+                       } else {
+                               return new ImageIcon(Files.readAllBytes(new File(source.toString()).toPath()));
+                       }
+               } catch (Throwable t) {
+                       return null;
+               }
+       }
+
+       /**
+        * Create a label that, when shown, displays the video.
+        * 
+        * @param source
+        * @return
+        */
+       public static JLabel createLabel(Object source) {
+               ImageIcon icon = (source instanceof ImageIcon ? (ImageIcon) source : createIcon(source));
+               return (icon == null ? null : new JLabel(icon));
+       }
+
+       /**
+        * Create a dialog that includes rudimentary controls. Optional maxWidth allows image downscaling by factors of two.
+        * 
+        * @param parent
+        * @param source 
+        * @param maxWidth
+        * @return
+        */
+       public static JDialog createDialog(Frame parent, Object source, int maxWidth, Function<HTML5Video, Void> whenReady) {
+               JDialog dialog = new JDialog(parent);
+               Container p = dialog.getContentPane();
+               p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
+               JLabel label = (source instanceof JLabel ? (JLabel) source : createLabel(source));
+               label.setAlignmentX(0.5f);
+               // not in Java! dialog.putClientProperty("jsvideo", label);
+               p.add(label);
+               label.setVisible(false);
+               p.add(getControls(label));
+               dialog.setModal(false);
+               dialog.pack();
+               dialog.setVisible(true);
+               dialog.setVisible(false);
+               HTML5Video jsvideo = (HTML5Video) label.getClientProperty("jsvideo");
+               HTML5Video.addActionListener(jsvideo, new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (label.getClientProperty("jsvideo.size") != null)
+                                       return;
+                               Dimension dim = HTML5Video.getSize(jsvideo);
+                               while (dim.width > maxWidth) {
+                                       dim.width /= 2;
+                                       dim.height /= 2;
+                               }
+                               label.putClientProperty("jsvideo.size", dim);
+                               label.setPreferredSize(dim);
+                               label.setVisible(true);
+//                             label.invalidate();
+                               dialog.pack();
+//                             dialog.setVisible(false);
+                               if (whenReady != null)
+                                       whenReady.apply(jsvideo);
+                       }
+                       
+               }, "canplaythrough");
+               HTML5Video.setCurrentTime(jsvideo,  0);
+               return dialog;
+       }
+
+       static JPanel getControls(JLabel label) {
+
+               JPanel controls = new JPanel();
+               controls.setAlignmentX(0.5f);
+               JButton btn = new JButton("play");
+               btn.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               try {
+                                       ((HTML5Video) label.getClientProperty("jsvideo")).play();
+                               } catch (Throwable e1) {
+                                       e1.printStackTrace();
+                               }
+                       }
+
+               });
+               controls.add(btn);
+
+               btn = new JButton("pause");
+               btn.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               try {
+                                       ((HTML5Video) label.getClientProperty("jsvideo")).pause();
+                               } catch (Throwable e1) {
+                                       e1.printStackTrace();
+                               }
+                       }
+
+               });
+               controls.add(btn);
+               
+               btn = new JButton("reset");
+               btn.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               HTML5Video.setCurrentTime((HTML5Video) label.getClientProperty("jsvideo"), 0);
+                       }
+
+               });
+               controls.add(btn);
+
+               return controls;
+       }
+
+       /**
+        * Advance to the next frame, using seekToNextFrame() if available, or using the time difference supplied.
+        * 
+        * @param jsvideo
+        * @param dt  seconds to advance if seekToNextFrame() is not available
+        * @return true if can use seekToNextFrame()
+        * 
+        */
+       public static boolean nextFrame(HTML5Video jsvideo, double dt) {
+               Boolean canSeek = (Boolean) getProperty(jsvideo,"_canseek");
+               if (canSeek == null) {
+                       setProperty(jsvideo, "_canseek", canSeek = Boolean.valueOf(getProperty(jsvideo, "seekToNextFrame") != null));
+               }
+               try {                   
+                       if (canSeek) {
+                               jsvideo.seekToNextFrame();
+                       } else {
+                               HTML5Video.setCurrentTime(jsvideo, HTML5Video.getCurrentTime(jsvideo) + dt);                                            
+                       }
+               } catch (Throwable e1) {
+               }
+               return canSeek.booleanValue();
+       }
+
+       public static int getFrameCount(HTML5Video jsvideo) {
+               return (int) (getDuration(jsvideo) / 0.033334);
+       }
+
+// HTMLMediaElement properties
+
+//     audioTracks
+//     autoplay
+//     buffered Read only
+//     controller
+//     controls
+//     controlsList Read only
+//     crossOrigin
+//     currentSrc Read only
+//     currentTime
+//     defaultMuted
+//     defaultPlaybackRate
+//     disableRemotePlayback
+//     duration Read only
+//     ended Read only
+//     error Read only
+//     loop
+//     mediaGroup
+//     mediaKeys Read only
+//     mozAudioCaptured Read only
+//     mozFragmentEnd
+//     mozFrameBufferLength
+//     mozSampleRate Read only
+//     muted
+//     networkState Read only
+//     paused Read only
+//     playbackRate
+//     played Read only
+//     preload
+//     preservesPitch
+//     readyState Read only
+//     seekable Read only
+//     seeking Read only
+//     sinkId Read only
+//     src
+//     srcObject
+//     textTracks Read only
+//     videoTracks Read only
+//     volume
+//     initialTime Read only
+//     mozChannels Read only
+
+}
diff --git a/src/swingjs/api/js/J2SInterface.java b/src/swingjs/api/js/J2SInterface.java
new file mode 100644 (file)
index 0000000..8051c96
--- /dev/null
@@ -0,0 +1,83 @@
+package swingjs.api.js;
+
+import java.awt.Component;
+import java.awt.Point;
+import java.util.Hashtable;
+
+
+/**
+ * An interface to J2S.xxx() functions.
+ * 
+ * @author hansonr
+ *
+ */
+
+public interface J2SInterface {
+
+       void addBinaryFileType(String ext);
+
+       void addDirectDatabaseCall(String domain);
+       
+       boolean debugClip();
+       
+       
+       
+       HTML5Applet findApplet(String htmlName);
+
+       Object getCachedJavaFile(String key);
+
+       /**
+        * 
+        * @param isAll true for check of navigator; otherwise just J2S._lang from j2sLang=xx_XX in URI
+        * @return
+        */
+       String getDefaultLanguage(boolean isAll);
+
+       Object getFileData(String fileName, Object fWhenDone, boolean doProcess, boolean isBinary);
+
+       void getFileFromDialog(Object fWhenDone, String type);
+
+       Object getJavaResource(String resourceName, boolean isJavaPath);
+       
+       String getJavaVersion();
+
+       int getKeyModifiers(Object jQueryEvent);
+       
+       Point getMousePosition(Point p);
+       
+       String getResourcePath(String resourceName, boolean isJavaPath);
+
+       Hashtable<String, Object> getSetJavaFileCache(Object object);
+       
+       Object getSwing(); // JSSwingMenu 
+       
+       int getZ(HTML5Applet applet, String frameType);
+
+       boolean isBinaryUrl(String filename);
+
+       boolean isResourceLoaded(String file, boolean done);
+
+       void readyCallback(String appId, String fullId, boolean isReady, 
+                       Object javaApplet, Object javaAppletPanel);
+
+       void saveFile(String fileName, Object data, String mimeType, String encoding);
+       
+       void setDragDropTarget(Component target, DOMNode node, boolean adding);
+
+       void setDraggable(DOMNode tagNode, Object targetNodeOrFDown);
+       
+       void setKeyListener(DOMNode node);
+
+       void setMouse(DOMNode frameNode, boolean isSwingJS);
+
+       int setWindowZIndex(DOMNode domNode, int pos);
+
+       void unsetMouse(DOMNode frameNode);
+
+       String fixCachePath(String uri);
+
+       void showStatus(String msg, boolean doFadeOut);
+
+
+}
+
diff --git a/src/swingjs/api/js/JQuery.java b/src/swingjs/api/js/JQuery.java
new file mode 100644 (file)
index 0000000..4b21993
--- /dev/null
@@ -0,0 +1,15 @@
+package swingjs.api.js;
+
+public interface JQuery {
+
+  JQueryObject $(Object selector);
+
+  DOMNode parseXML(String xmlData);
+  
+  boolean contains(Object outer, Object inner);
+
+  Object parseJSON(String json);
+
+  Object data(Object node, String attr);
+
+}
diff --git a/src/swingjs/api/js/JQueryObject.java b/src/swingjs/api/js/JQueryObject.java
new file mode 100644 (file)
index 0000000..3e53f6e
--- /dev/null
@@ -0,0 +1,85 @@
+package swingjs.api.js;
+
+public interface JQueryObject {
+
+       public interface JQEvent {
+
+       }
+
+       public abstract void appendTo(Object obj);
+       public abstract JQueryObject append(Object span);
+
+       public abstract void bind(String actions, Object f);
+       public abstract void unbind(String actions);
+
+       public abstract void on(String eventName, Object f);
+
+       public abstract JQueryObject focus();
+       public abstract JQueryObject select();
+
+       public abstract int width();
+       public abstract int height();
+       public abstract Object offset();
+
+
+       public abstract void html(String html);
+
+       public abstract DOMNode get(int i);
+
+       public abstract String attr(String key);
+       public abstract JQueryObject attr(String key, String value);
+       public abstract JQueryObject css(String key, String value);
+
+       public abstract JQueryObject addClass(String name);     
+       public abstract JQueryObject removeClass(String name);
+       
+       public abstract JQueryObject show();
+       public abstract JQueryObject hide();
+
+       public abstract void resize(Object fHandleResize);
+
+
+       /**
+        * closest ancestor
+        * 
+        * @param selector
+        * @return
+        */
+       public abstract JQueryObject closest(String selector);
+
+       /**
+        * find all descendants
+        * 
+        * @param selector
+        * @return
+        */
+       public abstract JQueryObject find(String selector);
+
+       public abstract JQueryObject parent();
+       public abstract void before(Object obj);
+       public abstract void after(Object div);
+
+       
+       /**
+        * remove from tree, but do not clear events
+        */
+       public abstract void detach(); // like remove(), but does not change event settings
+       
+       /**
+        * remove from tree and clear all events -- for disposal only
+        */
+       public abstract void remove();
+
+       /**
+        * fully remove all children, clearing all events
+        */
+       public abstract void empty();
+
+       public abstract DOMNode getElement();
+       
+       public static DOMNode getDOMNode(JQueryObject jnode) {
+               return (jnode == null ? null : ((DOMNode[]) (Object) jnode)[0]);
+       }
+       
+
+}
diff --git a/src/swingjs/api/js/JSFunction.java b/src/swingjs/api/js/JSFunction.java
new file mode 100644 (file)
index 0000000..41d1fb3
--- /dev/null
@@ -0,0 +1,12 @@
+package swingjs.api.js;
+
+/**
+ * A flag that this object is really a JavaScript function that, for example,
+ * might be called from setTimeout(). 
+ * 
+ * @author Bob Hanson
+ *
+ */
+public interface JSFunction {
+
+}
diff --git a/src/swingjs/api/js/JSInterface.java b/src/swingjs/api/js/JSInterface.java
new file mode 100644 (file)
index 0000000..f79ae8d
--- /dev/null
@@ -0,0 +1,43 @@
+package swingjs.api.js;
+
+/**
+ * called by SwingJS JavaScript methods
+ * 
+ */
+public interface JSInterface {
+
+       int cacheFileByName(String fileName, boolean isAdd); // $S$Z
+
+       void cachePut(String key, Object data); // $S$O
+
+       void destroy();
+
+       String getFullName();
+
+       void openFileAsyncSpecial(String fileName, int flags); // $S$I
+
+       boolean processMouseEvent(int id, int x, int y, int modifiers, long time, Object jqevent, int scroll); // $I$I$I$I$J$O$I
+
+       void processTwoPointGesture(float[][][] touches); // AFFF
+
+       void setDisplay(HTML5Canvas canvas);
+
+       void setScreenDimension(int width, int height);
+
+       boolean setStatusDragDropped(int mode, int x, int y, String fileName);
+
+       void startHoverWatcher(boolean enable);
+
+       static void setCursor(String c) {
+               /**
+                * @j2sNative
+                * 
+                * try {
+                * 
+                *   document.body.style.cursor = c;
+                * 
+                * } catch (e) {}
+                */
+       }
+
+}
diff --git a/src/swingjs/api/js/README.txt b/src/swingjs/api/js/README.txt
new file mode 100644 (file)
index 0000000..ed8abb4
--- /dev/null
@@ -0,0 +1,6 @@
+package swingjs.api.js
+
+This package contains interfaces to HTML5 objects and JavaScript functions that 
+are potentially useful to developers without use of  @j2sNative. 
+
+Caution should be used. Their persistence is not guaranteed.
\ No newline at end of file
index 6586c04..70b858b 100644 (file)
@@ -6,8 +6,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
-import java.util.PriorityQueue;
-import java.util.Random;
 import java.util.TreeSet;
 
 import javax.swing.event.TreeExpansionEvent;
@@ -22,11 +20,11 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
          private FragSeqNode _rootIDs = new FragSeqNode("IDs");
          private FragSeqNode _rootFolders = new FragSeqNode("Folders");
 
-       private TreeSet<String> _folders = new TreeSet<String>();
-  private Hashtable<String,FragSeqNode> _folderPathToFolderNode = new Hashtable<String,FragSeqNode>();
+       private TreeSet<String> _folders = new TreeSet<>();
+  private Hashtable<String,FragSeqNode> _folderPathToFolderNode = new Hashtable<>();
 
-  private Hashtable<String,FragSeqNode> _idsToNode = new Hashtable<String,FragSeqNode>();
-  private Hashtable<String,ArrayList<FragSeqNode>> _pathToIDFileNodes = new Hashtable<String,ArrayList<FragSeqNode>>();
+  private Hashtable<String,FragSeqNode> _idsToNode = new Hashtable<>();
+  private Hashtable<String,ArrayList<FragSeqNode>> _pathToIDFileNodes = new Hashtable<>();
   
   public enum SORT_MODE{
          PATH,
@@ -77,7 +75,10 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
                  if (parent.getChildCount()==0)
                  {
                          parent.removeFromParent();
-                         _folderPathToFolderNode.remove(parent);
+        // BH was just parent --
+        System.out.println("Varna FragSeqTreeModel removing " + parent + " "
+                + parent.toString());
+        _folderPathToFolderNode.remove(parent.toString());
                          if (parent.getUserObject() instanceof String)
                          {
                                  String path = parent.getUserObject().toString();
@@ -105,7 +106,7 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
 
   public void removeFolder(String path)
   {
-         ArrayList<FragSeqNode> toBeRemoved = new ArrayList<FragSeqNode>(); 
+         ArrayList<FragSeqNode> toBeRemoved = new ArrayList<>(); 
          Enumeration en = _folderPathToFolderNode.get(path).children();
          while(en.hasMoreElements())
          {
@@ -168,6 +169,7 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
          insertFileNode(_folderPathToFolderNode.get(folder), m);
   }
   
+  @Override
   public FragSeqNode getRoot()
   {
          return (FragSeqNode) super.getRoot();
@@ -175,13 +177,14 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
   
   public ArrayList<String> getFolders()
   {
-         ArrayList<String> result = new ArrayList<String>(_folders);
+         ArrayList<String> result = new ArrayList<>(_folders);
          return result;
   }
   
   
    FilenameFilter _f = new FilenameFilter(){
-               public boolean accept(File dir, String name) {
+               @Override
+    public boolean accept(File dir, String name) {
                        return name.toLowerCase().endsWith(".dbn") 
                        || name.toLowerCase().endsWith(".ct")
                        || name.toLowerCase().endsWith(".bpseq")
@@ -199,7 +202,7 @@ public class FragSeqTreeModel extends DefaultTreeModel implements TreeWillExpand
   }
 
   
-private Hashtable<FragSeqNode,Boolean> _isExpanded = new Hashtable<FragSeqNode,Boolean>();
+private Hashtable<FragSeqNode,Boolean> _isExpanded = new Hashtable<>();
   
 public boolean isExpanded(FragSeqNode n)
 {
@@ -208,9 +211,12 @@ public boolean isExpanded(FragSeqNode n)
        return _isExpanded.get(n);
   }
   else
-         return false;
+  {
+    return false;
+  }
 }
 
+@Override
 public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
        if (event.getSource() instanceof FragSeqTree)
        {
@@ -242,6 +248,7 @@ public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException
        }
 }
 
+@Override
 public void treeWillCollapse(TreeExpansionEvent event)
                throws ExpandVetoException {
        // TODO Auto-generated method stub
diff --git a/swingjs/README-JALVIEW b/swingjs/README-JALVIEW
new file mode 100644 (file)
index 0000000..735c35b
--- /dev/null
@@ -0,0 +1,23 @@
+@BobHanson The SwingJS-site.zip file is in ./swingjs as you'll expect. 
+It gets unzipped to a transfer location (and not to the actual site location 
+to avoid the unzip happening again unecessarily when something changes in that location, 
+e.g. an Eclipse IDE on-the-fly transpile) which is ./build/jalviewjs/tmp/site_swingjs/.
+
+In the meantime, if you're using Eclipse as IDE, it should be transpiling directly 
+into the actual site location, which is ./build/jalviewjs/site, and if you run the task 
+jalviewjsIDE_PrepareSite (under the Gradle Tasks tab) it will sync the files 
+from ./build/jalviewjs/tmp/site_swingjs/ to ./build/jalviewjs/site/ (along with other unzipped 
+files such as those in ./utils/jalviewjs/libjs/*.zip and files from ./utils/jalviewjs/site-resources/).
+
+If you run the task jalviewjsIDE_AssembleSite it will (/should) also build cores 
+into ./build/jalviewjs/tmp/site_core/ and then sync them to ./build/jalviewjs/site/.
+
+The _j2sclasslist.txt that gets used to build core_jalview.js is ./utils/jalviewjs/_j2sclasslist.txt, 
+whereas other lists are that are used to build cores are all the files found in ./utils/jalviewjs/classlists/.
+Basically all the stuff that gradle uses to build jalviewjs is found in ./utils/jalviewjs with 
+the exception of ./swingjs, which I know you wanted as-is in the top-level.
+
+
+
+
+
diff --git a/swingjs/ver/3.2.7/SwingJS-site.zip b/swingjs/ver/3.2.7/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..dd6765a
--- /dev/null
@@ -0,0 +1 @@
+Unexpected error.  File contents could not be restored from local history during undo/redo.
\ No newline at end of file
diff --git a/swingjs/ver/3.2.8/SwingJS-site.zip b/swingjs/ver/3.2.8/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..dd6765a
--- /dev/null
@@ -0,0 +1 @@
+Unexpected error.  File contents could not be restored from local history during undo/redo.
\ No newline at end of file
diff --git a/swingjs/ver/3.2.9-j11/SwingJS-site.zip b/swingjs/ver/3.2.9-j11/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..dd6765a
--- /dev/null
@@ -0,0 +1 @@
+Unexpected error.  File contents could not be restored from local history during undo/redo.
\ No newline at end of file
diff --git a/swingjs/ver/3.2.9-j11/differences.txt b/swingjs/ver/3.2.9-j11/differences.txt
new file mode 100644 (file)
index 0000000..46e49ec
--- /dev/null
@@ -0,0 +1,1484 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar b/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar
new file mode 100644 (file)
index 0000000..8cae046
Binary files /dev/null and b/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar differ
diff --git a/swingjs/ver/3.2.9-j11/timestamp b/swingjs/ver/3.2.9-j11/timestamp
new file mode 100644 (file)
index 0000000..7a0c426
--- /dev/null
@@ -0,0 +1 @@
+20201127032339 
diff --git a/swingjs/ver/3.2.9/SwingJS-site.zip b/swingjs/ver/3.2.9/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..52b640a
Binary files /dev/null and b/swingjs/ver/3.2.9/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.2.9/differences.txt b/swingjs/ver/3.2.9/differences.txt
new file mode 100644 (file)
index 0000000..60f5fcc
--- /dev/null
@@ -0,0 +1,1541 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+Restrictions on long
+Restriction on BitSet and Scanner
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+restrictions on long
+--------------------
+
+Java's 64-bit long type is not supported in JavaScript. There is no Int64Array in JavaScript,
+and 0x20000000000000 + 1 evaluates to 0x20000000000000, not 0x20000000000001. 
+(Likewise, -0x20000000000000 - 1 is left unchanged.) 
+
+The largest "integer" value in JavaScript is 9007199254740991 (9.007199254740991E13, or 0x1FFFFFFFFFFFFFF).
+Effectively, you get to use only 53 bits of the long, not 64. Trying to set a long larger than
+0x1FFFFFFFFFFFFFF or smaller than -0x1FFFFFFFFFFFFFF will result in a NumberFormatException.
+
+The transpiler handles conversion to long the same as Java for all cases other than from double. 
+
+For small double values, there is no problem, and, in fact, this is a known trick used to round 
+doubles and floats toward zero:
+
+double d;
+d = (long) 3.8;
+assert(d == 3);
+d = (long) -3.8;
+assert(d == -3);
+
+SwingJS will evaluate (long) d as 0 for d > 9007199254740991 
+or d < -9007199254740991, same as Java returns for Double.NaN.
+So, in Java we have:
+
+               assert(((long) Double.NaN) == 0);
+               assert(((int) Double.NaN) == 0);
+               assert(((long) Float.NaN) == 0);
+               assert(((int) Float.NaN) == 0);
+
+and also, in JavaScript only, we also have:
+
+               double d = 0x2000000000000L;
+               assert(((long) d) == 0);
+
+
+restrictions on BitSet and Scanner
+----------------------------------
+
+Because of the issue of long being only 53 bits, any time a method returns a long value, considerations must
+be made as to whether this will work in JavaScript. In particular, BitSet and Scanner have issues. 
+
+In SwingJS, java.util.BitSet has been implemented as a 32-bit integer-based bitset. This was no problem in
+Java 6, but starting with Java 7, a method was added to BitSet that allows for the extraction of the 
+underlying long[] word data. This is not work in JavaScript. Instead, SwingJS java.util.Bitset.toLongArray() will deliver 
+32-bit int[] data.
+
+SwingJS Scanner has hasNextLong() and nextLong(), and although it will scan through long numbers,
+Scanner will choke on long numbers greater than the JavaScript 53-bit limit. hasNextLong() will 
+return false, and nextLong() will throw an InputMismatchException triggered by the NumberFormatException
+thrown by Long.parseLong(). 
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/3.2.9/net.sf.j2s.core.jar b/swingjs/ver/3.2.9/net.sf.j2s.core.jar
new file mode 100644 (file)
index 0000000..ab277a9
Binary files /dev/null and b/swingjs/ver/3.2.9/net.sf.j2s.core.jar differ
diff --git a/swingjs/ver/3.2.9/timestamp b/swingjs/ver/3.2.9/timestamp
new file mode 100644 (file)
index 0000000..df5be44
--- /dev/null
@@ -0,0 +1 @@
+20201206115202 
diff --git a/temp/bbb.dtd.pdf b/temp/bbb.dtd.pdf
new file mode 100644 (file)
index 0000000..93f7302
Binary files /dev/null and b/temp/bbb.dtd.pdf differ
diff --git a/template.html b/template.html
new file mode 100644 (file)
index 0000000..d132460
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>SwingJS test _NAME_</title><meta charset="utf-8" />
+<script src="swingjs/swingjs2.js"></script>
+<script>
+if (!self.SwingJS)alert('swingjs2.js was not found. It needs to be in swingjs folder in the same directory as ' + document.location.href)
+Info = {
+  code: _CODE_,
+  main: _MAIN_,
+  core: "NONE",
+       width: 850,
+       height: 550,
+  readyFunction: null,
+       serverURL: 'https://chemapps.stolaf.edu/jmol/jsmol/php/jsmol.php',
+       j2sPath: 'swingjs/j2s',
+       console:'sysoutdiv',
+       allowjavascript: true
+}
+</script>
+</head>
+<body>
+<script>
+SwingJS.getApplet('testApplet', Info)
+getClassList = function(){J2S._saveFile('_j2sclasslist.txt', Clazz.ClassFilesLoaded.sort().join('\n'))}
+</script>
+<div style="position:absolute;left:900px;top:30px;width:600px;height:300px;">
+<div id="sysoutdiv" contentEditable="true" style="border:1px solid green;width:100%;height:95%;overflow:auto"></div>
+This is System.out. <a href="javascript:testApplet._clearConsole()">clear it</a>  <a href='javascript:J2S.getProfile()'>start/stop profiling</a><br>see <a href=___j2sflags.htm>___j2sflags.htm</a> for SwingJS URL command-line options<br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
+</div>
+</body>
+</html>
index 4974a88..1ddcfb4 100644 (file)
  */
 package jalview.analysis;
 
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.io.FastaFile;
-
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.PrintStream;
 import java.util.Arrays;
 import java.util.Random;
 
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.io.FastaFile;
+
 /**
  * Generates, and outputs in Fasta format, a random peptide or nucleotide alignment for given
  * sequence length and count. Will regenerate the same alignment each time if
index 3b9be23..a566be9 100644 (file)
@@ -31,7 +31,7 @@ public class AlignmentSorterTest
     /*
      * sort with no score features does nothing
      */
-    PA.setValue(AlignmentSorter.class, "sortByFeatureCriteria", null);
+    PA.setValue(AlignmentSorter.getInstance(), "sortByFeatureCriteria", null);
 
     AlignmentSorter.sortByFeature(null, null, 0, al.getWidth(), al,
             AlignmentSorter.FEATURE_SCORE);
@@ -64,7 +64,7 @@ public class AlignmentSorterTest
      * sort by ascending score, no filter on feature type or group
      * NB sort order for the same feature set (none) gets toggled, so descending
      */
-    PA.setValue(AlignmentSorter.class, "sortByFeatureAscending", true);
+    PA.setValue(AlignmentSorter.getInstance(), "sortByFeatureAscending", true);
     AlignmentSorter.sortByFeature(null, null, 0, al.getWidth(), al,
             AlignmentSorter.FEATURE_SCORE);
     assertSame(al.getSequenceAt(3), seq3); // -0.5
index cfd0938..b45547e 100644 (file)
@@ -27,6 +27,16 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
@@ -50,16 +60,6 @@ import jalview.io.gff.SequenceOntologyI;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
 public class AlignmentUtilsTests
 {
   private static Sequence ts = new Sequence("short",
index e5818a2..9a0d610 100644 (file)
@@ -28,6 +28,14 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 import jalview.datamodel.Alignment;
@@ -41,15 +49,6 @@ import jalview.gui.JvOptionPane;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.ws.SequenceFetcher;
-import jalview.ws.SequenceFetcherFactory;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
 
 public class CrossRefTest
 {
@@ -442,7 +441,7 @@ public class CrossRefTest
         return new SequenceI[] { pep1, pep2 };
       }
     };
-    SequenceFetcherFactory.setSequenceFetcher(mockFetcher);
+    SequenceFetcher.setMockFetcher(mockFetcher);
 
     /*
      * find UNIPROT xrefs for nucleotide sequence
@@ -458,7 +457,7 @@ public class CrossRefTest
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceFetcherFactory.setSequenceFetcher(null);
+    SequenceFetcher.setMockFetcher(null);
   }
 
   /**
@@ -520,7 +519,7 @@ public class CrossRefTest
         return new SequenceI[] { pep1, pep2 };
       }
     };
-    SequenceFetcherFactory.setSequenceFetcher(mockFetcher);
+    SequenceFetcher.setMockFetcher(mockFetcher);
 
     /*
      * find UNIPROT xrefs for gene and transcripts
@@ -680,7 +679,7 @@ public class CrossRefTest
         }
       }
     };
-    SequenceFetcherFactory.setSequenceFetcher(mockFetcher);
+    SequenceFetcher.setMockFetcher(mockFetcher);
 
     /*
      * find EMBL xrefs for Uniprot seqs and verify that
index 5f64b28..bd18abd 100644 (file)
@@ -68,7 +68,7 @@ public class FinderTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("PAD_GAPS",
+    Cache.setPropertyNoSave("PAD_GAPS",
             Boolean.FALSE.toString());
 
     String seqData = "seq1seq1/8-18 ABCD--EF-GHIJI\n" + "seq2 A--BCDefHI\n"
index 59fc79d..a5067be 100644 (file)
  */
 package jalview.bin;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatException;
+import jalview.io.FileFormatI;
+import jalview.io.FileFormats;
+import jalview.io.IdentifyFile;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -279,7 +286,7 @@ public class CommandLineOperations
     dataProvider = "headlessModeOutputOperationsData")
   public void testHeadlessModeOutputOperations(String harg, String type,
           String fileName, boolean withAWT, int expectedMinFileSize,
-          int timeout)
+          int timeout, String fileFormatType)
   {
     String cmd = harg + type + " " + fileName;
     // System.out.println(">>>>>>>>>>>>>>>> Command : " + cmd);
@@ -291,6 +298,25 @@ public class CommandLineOperations
     assertTrue(file.exists(), msg);
     FileAssert.assertFile(file, msg);
     FileAssert.assertMinLength(file, expectedMinFileSize);
+    if (fileFormatType!=null && fileFormatType.length()>0)
+    {
+      FileFormatI format = FileFormats.getInstance()
+              .forName(fileFormatType);
+      if (format!=null)
+      {
+        try
+        {
+          FileFormatI exportedType = new IdentifyFile()
+                  .identify(file.getAbsolutePath(), DataSourceType.FILE);
+          assertEquals(exportedType, format,
+                  "Exported file type was wrong");
+        } catch (FileFormatException e)
+        {
+          Assert.fail("Couldn't identify file " + file
+                  + " as an alignment format", e);
+        }
+      }
+    }
     if (worker != null && worker.exit == null)
     {
       worker.interrupt();
@@ -343,51 +369,51 @@ public class CommandLineOperations
     String workingDir = "test/jalview/bin/";
     return new Object[][] { { "nodisplay -open examples/uniref50.fa",
         " -eps", workingDir + "test_uniref50_out.eps", true,
-        MINFILESIZE_BIG, TEST_TIMEOUT },
+        MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "nodisplay -open examples/uniref50.fa", " -eps",
             workingDir + "test_uniref50_out.eps", false,
-            MINFILESIZE_BIG, TEST_TIMEOUT },
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "nogui -open examples/uniref50.fa", " -eps",
             workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, null },
         { "nogui -open examples/uniref50.fa", " -eps",
             workingDir + "test_uniref50_out.eps", false,
-            MINFILESIZE_BIG, TEST_TIMEOUT },
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -eps",
             workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -svg",
             workingDir + "test_uniref50_out.svg", false,
-            MINFILESIZE_BIG, TEST_TIMEOUT },
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -png",
             workingDir + "test_uniref50_out.png", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -html",
             workingDir + "test_uniref50_out.html", true,
-            MINFILESIZE_BIG, TEST_TIMEOUT },
+            MINFILESIZE_BIG, TEST_TIMEOUT, null },
         { "headless -open examples/uniref50.fa", " -fasta",
             workingDir + "test_uniref50_out.mfa", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Fasta.toString() },
         { "headless -open examples/uniref50.fa", " -clustal",
             workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Clustal.toString() },
         { "headless -open examples/uniref50.fa", " -msf",
             workingDir + "test_uniref50_out.msf", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.MSF.toString() },
         { "headless -open examples/uniref50.fa", " -pileup",
             workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Pileup.toString() },
         { "headless -open examples/uniref50.fa", " -pir",
             workingDir + "test_uniref50_out.pir", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.PIR.toString() },
         { "headless -open examples/uniref50.fa", " -pfam",
             workingDir + "test_uniref50_out.pfam", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.Pfam.toString() },
         { "headless -open examples/uniref50.fa", " -blc",
             workingDir + "test_uniref50_out.blc", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT },
+            TEST_TIMEOUT, FileFormat.BLC.toString() },
         { "headless -open examples/uniref50.fa", " -jalview",
             workingDir + "test_uniref50_out.jvp", true, MINFILESIZE_SMALL,
-            TEST_TIMEOUT }, };
+            TEST_TIMEOUT, FileFormat.Jalview.toString() }, };
   }
 }
index 4aa7e49..e7cdc6a 100644 (file)
@@ -28,13 +28,6 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.analysis.AlignmentGenerator;
-import jalview.commands.EditCommand;
-import jalview.commands.EditCommand.Action;
-import jalview.datamodel.PDBEntry.Type;
-import jalview.gui.JvOptionPane;
-import jalview.util.MapList;
-
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -48,6 +41,13 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import jalview.analysis.AlignmentGenerator;
+import jalview.commands.EditCommand;
+import jalview.commands.EditCommand.Action;
+import jalview.datamodel.PDBEntry.Type;
+import jalview.gui.JvOptionPane;
+import jalview.util.MapList;
+
 import junit.extensions.PA;
 
 public class SequenceTest
index 0846ec2..d9efa23 100644 (file)
@@ -44,7 +44,7 @@ public class FeatureAttributesTest
    * Test the method that keeps attribute names in non-case-sensitive order,
    * including handling of 'compound' names
    */
-  @Test(groups="Functional")
+  @Test(groups = "Functional")
   public void testAttributeNameComparator()
   {
     FeatureAttributes fa = FeatureAttributes.getInstance();
@@ -52,19 +52,24 @@ public class FeatureAttributesTest
             "comparator");
 
     assertEquals(
-            comp.compare(new String[] { "CSQ" }, new String[] { "csq" }), 0);
+            comp.compare(new String[]
+            { "CSQ" }, new String[] { "csq" }), 0);
 
-    assertTrue(comp.compare(new String[] { "CSQ", "a" },
-            new String[] { "csq" }) > 0);
+    assertTrue(
+            comp.compare(new String[]
+            { "CSQ", "a" }, new String[] { "csq" }) > 0);
 
-    assertTrue(comp.compare(new String[] { "CSQ" }, new String[] { "csq",
-        "b" }) < 0);
+    assertTrue(
+            comp.compare(new String[]
+            { "CSQ" }, new String[] { "csq", "b" }) < 0);
 
-    assertTrue(comp.compare(new String[] { "CSQ", "AF" }, new String[] {
-        "csq", "ac" }) > 0);
+    assertTrue(
+            comp.compare(new String[]
+            { "CSQ", "AF" }, new String[] { "csq", "ac" }) > 0);
 
-    assertTrue(comp.compare(new String[] { "CSQ", "ac" }, new String[] {
-        "csq", "AF" }) < 0);
+    assertTrue(
+            comp.compare(new String[]
+            { "CSQ", "ac" }, new String[] { "csq", "AF" }) < 0);
   }
 
   @Test(groups = "Functional")
@@ -124,10 +129,13 @@ public class FeatureAttributesTest
             "group");
     sf.setValue("kd", "-1");
     sf.setValue("domain", "Metal");
+    sf.setValue("foo", " ");
     sf.setValue("phase", "1");
-    sf.setValue("phase", "reverse");
+    sf.setValue("phase", "1reverse");
     assertEquals(fa.getDatatype("Pfam", "kd"), Datatype.Number);
     assertEquals(fa.getDatatype("Pfam", "domain"), Datatype.Character);
     assertEquals(fa.getDatatype("Pfam", "phase"), Datatype.Mixed);
+    assertNull(fa.getDatatype("Pfam", "unobserved"));
+    assertNull(fa.getDatatype("Pfam", "foo"));// empty values are ignored
   }
 }
index 4198a37..56512cd 100644 (file)
@@ -5,8 +5,6 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
-import jalview.datamodel.SequenceFeature;
-
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -15,6 +13,7 @@ import java.util.Set;
 
 import org.testng.annotations.Test;
 
+import jalview.datamodel.SequenceFeature;
 import junit.extensions.PA;
 
 public class SequenceFeaturesTest
@@ -1052,7 +1051,8 @@ public class SequenceFeaturesTest
   public void testSortFeatures()
   {
     List<SequenceFeature> sfs = new ArrayList<>();
-    SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30, 80,
+    SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30,
+            60,
             Float.NaN, null);
     sfs.add(sf1);
     SequenceFeature sf2 = new SequenceFeature("Rfam", "desc", 40, 50,
@@ -1061,18 +1061,34 @@ public class SequenceFeaturesTest
     SequenceFeature sf3 = new SequenceFeature("Rfam", "desc", 50, 60,
             Float.NaN, null);
     sfs.add(sf3);
+    SequenceFeature sf4 = new SequenceFeature("Xfam", "desc", 30,
+            80,
+            Float.NaN, null);
+    sfs.add(sf4);
+    SequenceFeature sf5 = new SequenceFeature("Xfam", "desc", 30,
+            90,
+            Float.NaN, null);
+    sfs.add(sf5);
 
-    // sort by end position descending
+    /*
+     * sort by end position descending, order unchanged if matched
+     */
     SequenceFeatures.sortFeatures(sfs, false);
-    assertSame(sfs.get(0), sf1);
-    assertSame(sfs.get(1), sf3);
-    assertSame(sfs.get(2), sf2);
+    assertSame(sfs.get(0), sf5); // end 90
+    assertSame(sfs.get(1), sf4); // end 80
+    assertSame(sfs.get(2), sf1); // end 60, start 50
+    assertSame(sfs.get(3), sf3); // end 60, start 30
+    assertSame(sfs.get(4), sf2); // end 50
 
-    // sort by start position ascending
+    /*
+     * resort {5, 4, 1, 3, 2} by start position ascending, end descending
+     */
     SequenceFeatures.sortFeatures(sfs, true);
-    assertSame(sfs.get(0), sf1);
-    assertSame(sfs.get(1), sf2);
-    assertSame(sfs.get(2), sf3);
+    assertSame(sfs.get(0), sf5); // start 30, end 90
+    assertSame(sfs.get(1), sf4); // start 30, end 80
+    assertSame(sfs.get(2), sf1); // start 30, end 60
+    assertSame(sfs.get(3), sf2); // start 40
+    assertSame(sfs.get(4), sf3); // start 50
   }
 
   @Test(groups = "Functional")
index 9e9d9a4..92fef42 100644 (file)
@@ -54,13 +54,13 @@ public class EnsemblCdnaTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index e7574eb..4521d9b 100644 (file)
@@ -53,13 +53,13 @@ public class EnsemblCdsTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index 8b1e840..da129ba 100644 (file)
@@ -56,13 +56,13 @@ public class EnsemblGeneTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index 11140f9..0a9cc64 100644 (file)
@@ -52,13 +52,13 @@ public class EnsemblGenomeTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   /**
index e17b4a6..a4d6d92 100644 (file)
@@ -135,13 +135,13 @@ public class EnsemblSeqProxyTest
   @BeforeClass(alwaysRun = true)
   public void setUp()
   {
-    SequenceOntologyFactory.setInstance(new SequenceOntologyLite());
+    SequenceOntologyFactory.setSequenceOntology(new SequenceOntologyLite());
   }
 
   @AfterClass(alwaysRun = true)
   public void tearDown()
   {
-    SequenceOntologyFactory.setInstance(null);
+    SequenceOntologyFactory.setSequenceOntology(null);
   }
 
   @DataProvider(name = "ens_seqs")
index e42b54f..8752f93 100644 (file)
@@ -61,7 +61,7 @@ public class JmolCommandsTest
     SequenceRenderer sr = new SequenceRenderer(af.getViewport());
     SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
     String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
-    StructureSelectionManager ssm = new StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(null);
 
     // need some mappings!
 
@@ -90,7 +90,7 @@ public class JmolCommandsTest
     SequenceRenderer sr = new SequenceRenderer(af.getViewport());
     SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
     String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
-    StructureSelectionManager ssm = new StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(null);
   
     /*
      * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
index 2832135..0977669 100644 (file)
@@ -98,11 +98,11 @@ public class JmolParserTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_TEMPFACT_ANN",
+    Cache.setPropertyNoSave("ADD_TEMPFACT_ANN",
             Boolean.FALSE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
     StructureImportSettings.setDefaultStructureFileFormat("PDB");
     StructureImportSettings
index e451ed2..7ef0d16 100644 (file)
@@ -74,7 +74,7 @@ public class JmolViewerTest
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @Test(groups = { "Functional" })
index 06a09df..a18a8a3 100644 (file)
@@ -184,7 +184,7 @@ public class ChimeraCommandsTest
     SequenceRenderer sr = new SequenceRenderer(af.getViewport());
     SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
     String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
-    StructureSelectionManager ssm = new StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(null);
 
     /*
      * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
index 734f7eb..194d34a 100644 (file)
@@ -95,7 +95,7 @@ public class JalviewChimeraView
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @AfterMethod(alwaysRun = true)
index 4390873..a5b26f1 100644 (file)
@@ -23,8 +23,6 @@ package jalview.fts.service.pdb;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.gui.JvOptionPane;
-
 import javax.swing.JInternalFrame;
 
 import org.testng.annotations.AfterMethod;
@@ -32,6 +30,7 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import jalview.gui.JvOptionPane;
 import junit.extensions.PA;
 
 public class PDBFTSPanelTest
index df9b762..31bd214 100644 (file)
@@ -77,7 +77,7 @@ public class AlignFrameTest
   @AfterMethod(alwaysRun = true)
   public void tearDown()
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   /**
@@ -88,7 +88,7 @@ public class AlignFrameTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.TRUE.toString());
     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
             DataSourceType.FILE);
index d6c875b..9c1a412 100644 (file)
@@ -57,6 +57,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.util.MapList;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
+import jalview.workers.AlignCalcManager;
 
 public class AlignViewportTest
 {
@@ -75,14 +76,16 @@ public class AlignViewportTest
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
   {
-    Jalview.main(new String[] { "-nonews", "-props",
+    Jalview.main(new String[] {
+        //"-jabaws", "none", 
+        "-nonews", "-props",
         "test/jalview/testProps.jvprops" });
 
     /*
      * remove any sequence mappings left lying around by other tests
      */
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
   }
 
@@ -130,7 +133,7 @@ public class AlignViewportTest
      * mappings
      */
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     List<AlignedCodonFrame> sequenceMappings = ssm.getSequenceMappings();
     assertEquals(2, sequenceMappings.size());
     assertTrue(sequenceMappings.contains(acf1));
@@ -152,10 +155,10 @@ public class AlignViewportTest
   @Test(groups = { "Functional" })
   public void testDeregisterMapping_withNoReference()
   {
-    Desktop d = Desktop.instance;
+    Desktop d = Desktop.getInstance();
     assertNotNull(d);
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
 
     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
@@ -215,10 +218,10 @@ public class AlignViewportTest
   @Test(groups = { "Functional" })
   public void testDeregisterMapping_withReference()
   {
-    Desktop d = Desktop.instance;
+    Desktop d = Desktop.getInstance();
     assertNotNull(d);
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
 
     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
@@ -282,15 +285,15 @@ public class AlignViewportTest
   @Test(groups = { "Functional" }, timeOut=2000)
   public void testUpdateConservation_qualityOnly()
   {
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_QUALITY",
+    Cache.setPropertyNoSave("SHOW_QUALITY",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+    Cache.setPropertyNoSave("SHOW_CONSERVATION",
             Boolean.FALSE.toString());
-    Cache.applicationProperties.setProperty("SHOW_OCCUPANCY",
+    Cache.setPropertyNoSave("SHOW_OCCUPANCY",
             Boolean.FALSE.toString());
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.FALSE.toString());
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
@@ -321,15 +324,19 @@ public class AlignViewportTest
   {
     synchronized (this)
     {
-      while (viewport.getCalcManager().isWorking())
+      System.out.print("waiting...");
+      int n = 3;
+      while (--n >= 0 || viewport.getCalcManager().isWorking())
       {
         try
         {
+          System.out.print(((AlignCalcManager) viewport.getCalcManager()).getQueueLength());
           wait(50);
         } catch (InterruptedException e)
         {
         }
       }
+           System.out.println("...done");
     }
   }
 
@@ -339,8 +346,8 @@ public class AlignViewportTest
     /*
      * test for JAL-2283: don't inadvertently turn on colour by conservation
      */
-    Cache.applicationProperties.setProperty("DEFAULT_COLOUR_PROT", "None");
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
+    Cache.setPropertyNoSave("DEFAULT_COLOUR_PROT", "None");
+    Cache.setPropertyNoSave("SHOW_CONSERVATION",
             Boolean.TRUE.toString());
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
@@ -482,7 +489,10 @@ public class AlignViewportTest
     AlignViewport testme = af.getViewport();
     waitForCalculations(testme);
     SequenceI cons = testme.getConsensusSeq();
-    assertEquals("A-C", cons.getSequenceAsString());
+    String s = cons.getSequenceAsString();
+    System.out.println("s is " + s);
+    
+    assertEquals("A-C", s);
   }
 
   @Test(groups = { "Functional" })
index 8871249..3d06bc8 100644 (file)
@@ -40,6 +40,7 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceI;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
+import jalview.util.Platform;
 import jalview.viewmodel.ViewportRanges;
 
 public class AlignmentPanelTest
@@ -49,10 +50,11 @@ public class AlignmentPanelTest
   @BeforeMethod(alwaysRun = true)
   public void setUp() throws InvocationTargetException, InterruptedException
   {
-    Jalview.main(new String[] { "-nonews", "-props",
-        "test/jalview/testProps.jvprops" });
+    Jalview.main(new String[] { "-nonews", 
+        "-props", "test/jalview/testProps.jvprops",
+        "-jabaws", "none"});
 
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+    Cache.setPropertyNoSave("SHOW_IDENTITY",
             Boolean.TRUE.toString());
     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
             DataSourceType.FILE);
@@ -94,22 +96,29 @@ public class AlignmentPanelTest
     af.alignPanel.setScrollValues(-1, 5);
 
     // setting -ve x value does not change residue
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres);
 
     af.alignPanel.setScrollValues(0, 5);
-
+    // no update necessary now
     // setting 0 as x value does not change residue
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres);
 
     af.alignPanel.setScrollValues(5, 5);
     // setting x value to 5 extends endRes by 5 residues
+    System.out.println(ranges);
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres + 5);
 
     // scroll to position after hidden columns sets endres to oldres (width) +
     // position
-    int scrollpos = 60;
+
+    int scrollpos = 53; // was 60, but this is too high to allow full scrolling
+                        // in Windows
     af.getViewport().hideColumns(30, 50);
     af.alignPanel.setScrollValues(scrollpos, 5);
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres + scrollpos);
 
     // scroll to position within hidden columns, still sets endres to oldres +
@@ -120,6 +129,7 @@ public class AlignmentPanelTest
     af.getViewport().showAllHiddenColumns();
     af.getViewport().hideColumns(30, 50);
     af.alignPanel.setScrollValues(scrollpos, 5);
+    // no update necessary now
     assertEquals(ranges.getEndRes(), oldres + scrollpos);
 
     // scroll to position within <width> distance of the end of the alignment
@@ -127,8 +137,9 @@ public class AlignmentPanelTest
     scrollpos = 130;
     af.getViewport().showAllHiddenColumns();
     af.alignPanel.setScrollValues(scrollpos, 5);
-    assertEquals(ranges.getEndRes(), af.getViewport()
-            .getAlignment().getWidth() - 1);
+    // no update necessary now
+    assertEquals(ranges.getEndRes(),
+            af.getViewport().getAlignment().getWidth() - 1);
 
     // now hide some columns, and scroll to position within <width>
     // distance of the end of the alignment
@@ -136,9 +147,12 @@ public class AlignmentPanelTest
     // columns
     af.getViewport().hideColumns(30, 50);
     af.alignPanel.setScrollValues(scrollpos, 5);
-    assertEquals(ranges.getEndRes(), af.getViewport()
-            .getAlignment().getWidth() - 1 - 21); // 21 is the number of hidden
-                                                  // columns
+    // no update necessary now
+    assertEquals(ranges.getEndRes(),
+            af.getViewport().getAlignment().getWidth() - 1 - 21); // 21 is the
+                                                                  // number of
+                                                                  // hidden
+                                                                  // columns
   }
 
   /**
@@ -192,8 +206,9 @@ public class AlignmentPanelTest
      * note 4 pixels padding are added to the longest sequence name width
      */
     av.setIdWidth(-1); // force recalculation
+    
     d = af.alignPanel.calculateIdWidth();
-    assertEquals(d.width, 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
+    assertEquals(d.width, Platform.isWin() ? 172 : 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
     assertEquals(d.height, 12);
     assertEquals(d.width, av.getIdWidth());
   }
@@ -215,7 +230,7 @@ public class AlignmentPanelTest
      * note 4 pixels 'padding' are added to the longest seq name/annotation label
      */
     Dimension d = af.alignPanel.calculateIdWidth(2000);
-    assertEquals(d.width, 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
+    assertEquals(d.width, Platform.isWin() ? 172 : 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
     assertEquals(d.height, 12); // fixed value (not used?)
     assertEquals(av.getIdWidth(), 18); // not changed by this method
 
@@ -224,11 +239,14 @@ public class AlignmentPanelTest
      */
     SequenceI seq = af.viewport.getAlignment()
             .findSequenceMatch("Q93Z60_ARATH")[0];
-    seq.setName(seq.getName() + "MMMMM");
+    String orig = seq.getName();
+    seq.setName(orig + "MMMMM");
     d = af.alignPanel.calculateIdWidth(2000);
-    assertEquals(d.width, 211); // 4 + pixel width of "Q93Z60_ARATHMMMMM/1-118"
+    assertEquals(d.width, Platform.isWin() ? 219 : 211); // 4 + pixel width of "Q93Z60_ARATHMMMMM/1-118"
     assertEquals(d.height, 12);
     assertEquals(av.getIdWidth(), 18); // unchanged
+    // for next test:
+    seq.setName(orig);
 
     /*
      * make the longest annotation name even longer
@@ -239,17 +257,19 @@ public class AlignmentPanelTest
     FontMetrics fmfor = af.alignPanel
             .getFontMetrics(af.alignPanel.getAlabels().getFont());
     // Assumption ID_WIDTH_PADDING == 4
+    // AH! But with those added MMMM above, this was NOT the longest label!
     int expwidth = 4 + fmfor.stringWidth(aa.label);
     d = af.alignPanel.calculateIdWidth(2000);
-    assertEquals(d.width, expwidth); // 228 == ID_WIDTH_PADDING + pixel width of "THIS IS A VERY LONG LABEL INDEED"
+    assertEquals(d.width, expwidth); // 191 == ID_WIDTH_PADDING + pixel width of "THIS IS A VERY LONG LABEL INDEED"
     assertEquals(d.height, 12);
 
     /*
      * override with maxwidth
      * note the 4 pixels padding is added to this value
      */
-    d = af.alignPanel.calculateIdWidth(213);
-    assertEquals(d.width, 217);
+    // BH but we have to be under the max width
+    d = af.alignPanel.calculateIdWidth(180);
+    assertEquals(d.width, 184);
     assertEquals(d.height, 12);
   }
 
@@ -261,7 +281,7 @@ public class AlignmentPanelTest
      */
     int w = af.alignPanel.getVisibleIdWidth(true);
     assertEquals(w, af.alignPanel.getIdPanel().getWidth());
-    assertEquals(w, 115);
+    assertEquals(w, Platform.isWin() ? 112 : 115);
 
     /*
      * width for offscreen rendering is the same
@@ -281,6 +301,6 @@ public class AlignmentPanelTest
      * preference for auto id width overrides fixed width
      */
     Cache.setProperty("FIGURE_AUTOIDWIDTH", Boolean.TRUE.toString());
-    assertEquals(115, af.alignPanel.getVisibleIdWidth(false));
+    assertEquals(Platform.isWin() ? 106 : 115, af.alignPanel.getVisibleIdWidth(false));
   }
 }
index fbdda09..f0c4632 100644 (file)
@@ -88,14 +88,14 @@ public class AnnotationChooserTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // pin down annotation sort order for test
-    Cache.applicationProperties.setProperty(Preferences.SORT_ANNOTATIONS,
+    Cache.setPropertyNoSave(Preferences.SORT_ANNOTATIONS,
             SequenceAnnotationOrder.NONE.name());
     final String TRUE = Boolean.TRUE.toString();
-    Cache.applicationProperties.setProperty(
+    Cache.setPropertyNoSave(
             Preferences.SHOW_AUTOCALC_ABOVE, TRUE);
-    Cache.applicationProperties.setProperty("SHOW_QUALITY", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY", TRUE);
+    Cache.setPropertyNoSave("SHOW_QUALITY", TRUE);
+    Cache.setPropertyNoSave("SHOW_CONSERVATION", TRUE);
+    Cache.setPropertyNoSave("SHOW_IDENTITY", TRUE);
 
     AlignmentI al = new FormatAdapter().readFile(TEST_DATA,
             DataSourceType.PASTE, FileFormat.Fasta);
index 912cd27..36d49dc 100644 (file)
@@ -76,14 +76,14 @@ public class AnnotationColumnChooserTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // pin down annotation sort order for test
-    Cache.applicationProperties.setProperty(Preferences.SORT_ANNOTATIONS,
+    Cache.setPropertyNoSave(Preferences.SORT_ANNOTATIONS,
             SequenceAnnotationOrder.NONE.name());
     final String TRUE = Boolean.TRUE.toString();
-    Cache.applicationProperties.setProperty(Preferences.SHOW_AUTOCALC_ABOVE,
+    Cache.setPropertyNoSave(Preferences.SHOW_AUTOCALC_ABOVE,
             TRUE);
-    Cache.applicationProperties.setProperty("SHOW_QUALITY", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION", TRUE);
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY", TRUE);
+    Cache.setPropertyNoSave("SHOW_QUALITY", TRUE);
+    Cache.setPropertyNoSave("SHOW_CONSERVATION", TRUE);
+    Cache.setPropertyNoSave("SHOW_IDENTITY", TRUE);
 
     AlignmentI al = new FormatAdapter().readFile(TEST_DATA,
             DataSourceType.PASTE, FileFormat.Fasta);
index 69a41c5..47bd924 100644 (file)
@@ -27,9 +27,9 @@ public class AnnotationRowFilterTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty(
+    Cache.setPropertyNoSave(
             Preferences.SHOW_AUTOCALC_ABOVE, Boolean.TRUE.toString());
     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
             DataSourceType.FILE);
index 6c2e777..595baef 100644 (file)
@@ -19,7 +19,7 @@ public class CalculationChooserTest
   {
     // read-only Jalview properties
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("BLOSUM62_PCA_FOR_NUCLEOTIDE",
+    Cache.setPropertyNoSave("BLOSUM62_PCA_FOR_NUCLEOTIDE",
             Boolean.FALSE.toString());
   }
 
@@ -73,7 +73,7 @@ public class CalculationChooserTest
     /*
      * enable inclusion of BLOSUM62 for nucleotide PCA (JAL-2962)
      */
-    Cache.applicationProperties.setProperty("BLOSUM62_PCA_FOR_NUCLEOTIDE",
+    Cache.setPropertyNoSave("BLOSUM62_PCA_FOR_NUCLEOTIDE",
             Boolean.TRUE.toString());
 
     /*
index 24697c0..66e0d22 100644 (file)
@@ -35,11 +35,11 @@ public class FreeUpMemoryTest
     Jalview.main(new String[] { "-nonews", "-props",
         "test/jalview/testProps.jvprops" });
     String True = Boolean.TRUE.toString();
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", True);
-    Cache.applicationProperties.setProperty("SHOW_QUALITY", True);
-    Cache.applicationProperties.setProperty("SHOW_CONSERVATION", True);
-    Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", True);
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY", True);
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS", True);
+    Cache.setPropertyNoSave("SHOW_QUALITY", True);
+    Cache.setPropertyNoSave("SHOW_CONSERVATION", True);
+    Cache.setPropertyNoSave("SHOW_OCCUPANCY", True);
+    Cache.setPropertyNoSave("SHOW_IDENTITY", True);
   }
 
   /**
@@ -77,7 +77,7 @@ public class FreeUpMemoryTest
     }
     doStuffInJalview(f);
 
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
 
     checkUsedMemory(expectedMin);
   }
@@ -120,7 +120,7 @@ public class FreeUpMemoryTest
      * sanity check - fails if any frame was added after
      * closeAll_actionPerformed
      */
-    assertEquals(Desktop.instance.getAllFrames().length, 0);
+    assertEquals(Desktop.getInstance().getAllFrames().length, 0);
 
     /*
      * if this assertion fails
index 61c5c9b..214a3ad 100644 (file)
@@ -86,7 +86,12 @@ public class JvSwingUtilsTest
             JvSwingUtils.wrapTooltip(true, tip));
 
     tip = "0123456789012345678901234567890123456789012345678901234567890"; // 61
-    assertFalse(tip.equals(JvSwingUtils.wrapTooltip(false, tip)));
+
+    // n/a -- message is too long for "false"
+//  
+//    assertFalse(tip.equals(JvSwingUtils.wrapTooltip(false, tip)));
+//    
+    
     assertFalse(("<html>" + tip + "</html>").equals(JvSwingUtils
             .wrapTooltip(true, tip)));
   }
@@ -100,9 +105,21 @@ public class JvSwingUtilsTest
   public void testWrapTooltip_multilineShortText()
   {
     String tip = "Now is the winter of our discontent<br>Made glorious summer by this sun of York";
-    assertEquals(tip, JvSwingUtils.wrapTooltip(false, tip));
-    assertEquals("<html>" + tip + "</html>",
-            JvSwingUtils.wrapTooltip(true, tip));
+    String tip2 = "Now is the winter of our discontent<br/>Made glorious summer by this sun of York";
+
+// BH not applicable in Jalview; "false" is only for when no <br> and only for short j2s2Discover messages
+//
+//    String s = JvSwingUtils.wrapTooltip(false, tip);
+//    System.out.println("<html>" + tip + "</html>");
+//    assertEquals(tip, s);
+    
+    String s;
+    s = JvSwingUtils.wrapTooltip(true, tip);
+    System.out.println(s);
+    assertEquals("<html>" + tip + "</html>", s);
+    s = JvSwingUtils.wrapTooltip(true, tip2);
+    System.out.println(s);
+    assertEquals("<html>" + tip + "</html>", s);
   }
 
   /**
@@ -112,11 +129,13 @@ public class JvSwingUtilsTest
   @Test(groups = { "Functional" })
   public void testWrapTooltip_longText()
   {
+    // BH should work in Java and JavaScript
     String tip = "Now is the winter of our discontent made glorious summer by this sun of York";
-    String expected = "<style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
-            + "<div class=\"ttip\">" + tip + " </div>";
-    assertEquals("<html>" + expected + "</html>",
-            JvSwingUtils.wrapTooltip(true, tip));
-    assertEquals(expected, JvSwingUtils.wrapTooltip(false, tip));
+    String expected = JvSwingUtils.HTML_PREFIX + tip + "</div></html>";
+    String s = JvSwingUtils.wrapTooltip(true, tip);
+    assertEquals(expected, s);
+ // BH not applicable in Jalview; "false" is only for when no <br> and only for short j2s2Discover messages
+//    s = JvSwingUtils.wrapTooltip(false, tip);
+//    assertEquals(expected, s);
   }
 }
index 4457eea..edf8d40 100644 (file)
@@ -37,7 +37,7 @@ public class PairwiseAlignmentPanelTest
 
     PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport);
 
-    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText();
+    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText().replace("\r\n", "\n");
     String expected = "Score = 80.0\n" + "Length of alignment = 4\n"
             + "Sequence     FER1_PEA/29-32 (Sequence length = 7)\n"
             + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n"
@@ -62,7 +62,7 @@ public class PairwiseAlignmentPanelTest
 
     PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport);
 
-    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText();
+    String text = ((JTextArea) PA.getValue(testee, "textarea")).getText().replace("\r\n", "\n");
     String expected = "Score = 80.0\n" + "Length of alignment = 4\n"
             + "Sequence     FER1_PEA/29-32 (Sequence length = 7)\n"
             + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n"
index 28b5728..49f9d7f 100644 (file)
@@ -201,9 +201,10 @@ public class PopupMenuTest
     testee.configureReferenceAnnotationsMenu(menu, seqs);
     assertTrue(menu.isEnabled());
     String s = MessageManager.getString("label.add_annotations_for");
-    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
-            + "<div class=\"ttip\">" + s
-            + "<br/>Jmol/secondary structure<br/>PDB/Temp </div></html>";
+//    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
+//            + "<div class=\"ttip\">" + s
+//            + "<br/>Jmol/secondary structure<br/>PDB/Temp </div></html>";
+    String expected = "<html>" + s + "<br>Jmol/secondary structure<br>PDB/Temp</html>";
     assertEquals(expected, menu.getToolTipText());
   }
 
@@ -225,10 +226,13 @@ public class PopupMenuTest
     testee.configureReferenceAnnotationsMenu(menu, seqs);
     assertTrue(menu.isEnabled());
     String s = MessageManager.getString("label.add_annotations_for");
-    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
-            + "<div class=\"ttip\">" + s
-            + "<br/>Jmol/secondary structure<br/>PDB/Temp </div></html>";
-    assertEquals(expected, menu.getToolTipText());
+//    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
+//            + "<div class=\"ttip\">" + s
+//            + "<br/>Jmol/secondary structure<br/>PDB/Temp</html>";
+    String expected = "<html>" + s
+            + "<br>Jmol/secondary structure<br>PDB/Temp</html>";
+    s = menu.getToolTipText();
+    assertEquals(expected, s);
   }
 
   /**
index 22035f1..cd37b61 100644 (file)
 package jalview.gui;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
 
 import java.awt.Font;
 import java.awt.FontMetrics;
 
-import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
+import jalview.util.Platform;
+import jalview.viewmodel.ViewportRanges;
 import junit.extensions.PA;
 
 public class SeqCanvasTest
 {
-  private AlignFrame af;
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties(null);
+    Cache.initLogger();
+    Desktop.getInstance().setVisible(false);
+  }
 
   /**
    * Test the method that computes wrapped width in residues, height of wrapped
@@ -45,6 +59,9 @@ public class SeqCanvasTest
   @Test(groups = "Functional")
   public void testCalculateWrappedGeometry_noAnnotations()
   {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
+    
     AlignViewport av = af.getViewport();
     AlignmentI al = av.getAlignment();
     assertEquals(al.getWidth(), 157);
@@ -57,8 +74,8 @@ public class SeqCanvasTest
     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
-    assertEquals(charHeight, 17);
-    assertEquals(charWidth, 12);
+    assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
+    assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
 
     /*
      * first with scales above, left, right
@@ -69,7 +86,8 @@ public class SeqCanvasTest
     av.setScaleRightWrapped(true);
     FontMetrics fm = testee.getFontMetrics(av.getFont());
     int labelWidth = fm.stringWidth("000") + charWidth;
-    assertEquals(labelWidth, 39); // 3 x 9 + charWidth
+    assertEquals(labelWidth,
+            !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
 
     /*
      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
@@ -191,7 +209,7 @@ public class SeqCanvasTest
     canvasWidth += 2;
     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
             canvasHeight);
-    assertEquals(wrappedWidth, 24); // 2px not enough
+    assertEquals(wrappedWidth, !Platform.isWin() ? 24 : 25); // 2px not enough
     canvasWidth += 1;
     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
             canvasHeight);
@@ -213,6 +231,8 @@ public class SeqCanvasTest
   @Test(groups = "Functional")
   public void testCalculateWrappedGeometry_withAnnotations()
   {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
     AlignViewport av = af.getViewport();
     AlignmentI al = av.getAlignment();
     assertEquals(al.getWidth(), 157);
@@ -223,9 +243,10 @@ public class SeqCanvasTest
     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
-    assertEquals(charHeight, 17);
-    assertEquals(charWidth, 12);
-  
+
+    assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
+    assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
+
     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
   
     /*
@@ -235,9 +256,12 @@ public class SeqCanvasTest
     av.setScaleAboveWrapped(true);
     av.setScaleLeftWrapped(true);
     av.setScaleRightWrapped(true);
+
     FontMetrics fm = testee.getFontMetrics(av.getFont());
     int labelWidth = fm.stringWidth("000") + charWidth;
-    assertEquals(labelWidth, 39); // 3 x 9 + charWidth
+    assertEquals(labelWidth,
+            !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
+
     int annotationHeight = testee.getAnnotationHeight();
 
     /*
@@ -309,53 +333,66 @@ public class SeqCanvasTest
   @Test(groups = "Functional")
   public void testCalculateWrappedGeometry_fromScrolled()
   {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
+    // 
+    System.out.println("ap dim = " + af.alignPanel.getSize());
+    System.out.println("seqpan dim = " + af.alignPanel.getSeqPanel().getSize());
+    System.out.println("seqcan dim = " + af.alignPanel.getSeqPanel().seqCanvas.getSize());
+
+
     AlignViewport av = af.getViewport();
     AlignmentI al = av.getAlignment();
     assertEquals(al.getWidth(), 157);
     assertEquals(al.getHeight(), 15);
-    av.getRanges().setStartEndSeq(0, 3);
-    av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
-    av.setWrapAlignment(true);
+
+    ViewportRanges ranges = av.getRanges();
+    System.out.println(ranges + " before setting to 3");
+    ranges.setStartEndSeq(0, 3);
+    int endSeq = ranges.getEndSeq();
+    System.out.println(ranges + " after setting to 3");
     av.setShowAnnotation(false);
     av.setScaleAboveWrapped(true);
 
     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
-
+    av.setWrapAlignment(true);
+    av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
     int charHeight = av.getCharHeight();
     int charWidth = av.getCharWidth();
-    assertEquals(charHeight, 17);
-    assertEquals(charWidth, 12);
-
+    // Windows h=19, w=11.
+    assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
+    assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
+    
     int canvasWidth = 400;
     int canvasHeight = 300;
     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
-
-    assertEquals(av.getRanges().getEndSeq(), 3); // unchanged
+    System.out.println(ranges);
+    assertEquals(ranges.getEndSeq(), endSeq); // unchanged
     int repeatingHeight = (int) PA.getValue(testee,
             "wrappedRepeatHeightPx");
-    assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
+    int h = charHeight * (2 + al.getHeight());
+    assertEquals(repeatingHeight, h);
   }
-
-  @BeforeMethod(alwaysRun = true)
-  public void setUp()
+  @Test(groups = "Functional")
+  public void testClear_HighlightAndSelection()
   {
-    Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
-            Boolean.TRUE.toString());
-    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
-            DataSourceType.FILE);
-  
-    /*
-     * wait for Consensus thread to complete
-     */
-    do
-    {
-      try
-      {
-        Thread.sleep(50);
-      } catch (InterruptedException x)
-      {
-      }
-    } while (af.getViewport().getCalcManager().isWorking());
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
+    AlignViewport av = af.getViewport();
+    SearchResultsI highlight = new SearchResults();
+    highlight.addResult(
+            av.getAlignment().getSequenceAt(1).getDatasetSequence(), 50,
+            80);
+    af.alignPanel.highlightSearchResults(highlight);
+    af.avc.markHighlightedColumns(false, false, false);
+    assertNotNull(av.getSearchResults(),
+            "No highlight was created on alignment");
+    assertFalse(av.getColumnSelection().isEmpty(),
+            "No selection was created from highlight");
+    af.deselectAllSequenceMenuItem_actionPerformed(null);
+    assertTrue(av.getColumnSelection().isEmpty(),
+            "No Selection should be present after deselecting all.");
+    assertNull(av.getSearchResults(),
+            "No higlighted search results should be present after deselecting all.");
   }
 }
index a03819d..01eec32 100644 (file)
@@ -252,14 +252,14 @@ public class SeqPanelTest
   @AfterMethod(alwaysRun = true)
   public void tearDown()
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @Test(groups = "Functional")
   public void testFindMousePosition_wrapped_annotations()
   {
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "true");
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS", "true");
+    Cache.setPropertyNoSave("WRAP_ALIGNMENT", "true");
     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     AlignViewportI av = alignFrame.getViewport();
@@ -433,8 +433,8 @@ public class SeqPanelTest
   @Test(groups = "Functional")
   public void testFindMousePosition_wrapped_scaleAbove()
   {
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "true");
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS", "true");
+    Cache.setPropertyNoSave("WRAP_ALIGNMENT", "true");
     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     AlignViewportI av = alignFrame.getViewport();
@@ -633,8 +633,8 @@ public class SeqPanelTest
   @Test(groups = "Functional")
   public void testFindMousePosition_wrapped_noAnnotations()
   {
-    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "false");
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+    Cache.setPropertyNoSave("SHOW_ANNOTATIONS", "false");
+    Cache.setPropertyNoSave("WRAP_ALIGNMENT", "true");
     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     AlignViewportI av = alignFrame.getViewport();
@@ -721,7 +721,7 @@ public class SeqPanelTest
   @Test(groups = "Functional")
   public void testFindColumn_unwrapped()
   {
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "false");
+    Cache.setPropertyNoSave("WRAP_ALIGNMENT", "false");
     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
@@ -790,7 +790,7 @@ public class SeqPanelTest
   @Test(groups = "Functional")
   public void testFindColumn_wrapped()
   {
-    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+    Cache.setPropertyNoSave("WRAP_ALIGNMENT", "true");
     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     AlignViewport av = alignFrame.getViewport();
index 0597568..42c8826 100644 (file)
@@ -24,14 +24,6 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.fts.api.FTSData;
-import jalview.jbgui.GStructureChooser.FilterOption;
-
 import java.util.Collection;
 import java.util.Vector;
 
@@ -40,6 +32,13 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.jbgui.GStructureChooser.FilterOption;
 import junit.extensions.PA;
 
 public class StructureChooserTest
index e3b7067..04cc3be 100644 (file)
@@ -48,7 +48,7 @@ public class HMMERTest {
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   /**
index 8eb3796..beeb52f 100644 (file)
@@ -68,9 +68,9 @@ public class AnnotatedPDBFileInputTest
   @BeforeMethod(alwaysRun = true)
   public void setup() throws Exception
   {
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
     FileLoader loader = new FileLoader(false);
     AlignFrame af = loader.LoadFileWaitTillLoaded("examples/1gaq.txt",
@@ -207,7 +207,7 @@ public class AnnotatedPDBFileInputTest
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
 
   }
 
diff --git a/test/jalview/io/BSMLFileTest.java b/test/jalview/io/BSMLFileTest.java
new file mode 100644 (file)
index 0000000..3052658
--- /dev/null
@@ -0,0 +1,68 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeaturesI;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class BSMLFileTest
+{
+  @Test(groups="Functional")
+  public void testParse_BSML() throws IOException
+  {
+    //@formatter:off
+    String data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
+            "<Bsml>\r\n" + 
+            "  <Definitions>\r\n" + 
+            "    <Sequences>\r\n" + 
+            "      <Sequence\r\n" + 
+            "        comment=\"Sequence Data Contained in 'Multiple-alignment' section\"\r\n" + 
+            "        id=\"SEQ-ID:181170\" length=\"30\" molecule=\"dna\" title=\"EP1\">\r\n" + 
+            "        <Feature-tables>\r\n" + 
+            "          <Feature-table title=\"GENE\">\r\n" + 
+            "            <Feature class=\"GENE\" title=\"TEST-001\">\r\n" + 
+            "              <Interval-loc complement=\"0\" endpos=\"20\" startpos=\"10\"/>\r\n" + 
+            "              <Resource id=\"GENE-ID:181171\"/>\r\n" + 
+            "            </Feature>\r\n" + 
+            "          </Feature-table>\r\n" + 
+            "        </Feature-tables>\r\n" + 
+            "      </Sequence>\r\n" + 
+            "    </Sequences>\r\n" + 
+            "    <Tables>\r\n" + 
+            "      <Multiple-alignment-table molecule-type=\"nucleotide\">\r\n" + 
+            "        <Sequence-alignment sequences=\"1\">\r\n" + 
+            "          <Sequence-data seq-name=\"EP1\">--AATTTT-ATTTAGTGTCT-----------</Sequence-data>\r\n" + 
+            "          <Alignment-consensus/>\r\n" + 
+            "        </Sequence-alignment>\r\n" + 
+            "      </Multiple-alignment-table>\r\n" + 
+            "    </Tables>\r\n" + 
+            "    <Notebook notebook=\"\"/>\r\n" + 
+            "  </Definitions>\r\n" + 
+            "</Bsml>\r\n" + 
+            "";
+    BSMLFile cf = new BSMLFile(data, DataSourceType.PASTE);
+// why twice?    cf.parse();
+    SequenceI[] seqs = cf.getSeqsAsArray();
+    assertEquals(seqs.length, 1);
+    SequenceI seq = seqs[0];
+    assertEquals(seq.getName(), "EP1");
+    assertEquals(seq.getStart(), 1);
+    assertEquals(seq.getEnd(), 31);
+    assertTrue(seq.getSequenceAsString().equals("--AATTTT-ATTTAGTGTCT-----------"));
+    SequenceFeaturesI features = seq.getFeatures();
+    List<SequenceFeature> genes = features.getAllFeatures("GENE");
+    assertEquals(genes.size(), 1);
+    SequenceFeature sf = genes.get(0);
+    assertEquals(sf.getDescription(), "TEST-001");
+    assertEquals(sf.begin + "," + sf.end, "10,20");
+    
+  }
+
+}
index 64cf902..c84285a 100644 (file)
@@ -213,23 +213,23 @@ public class BackupFilesTest
     BackupFilesPresetEntry bfpe = new BackupFilesPresetEntry(suffix, digits,
             reverse, noMax, rollMax, false);
 
-    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+    Cache.setPropertyNoSave(BackupFiles.ENABLED,
             Boolean.toString(enabled));
-    Cache.applicationProperties.setProperty(
+    Cache.setPropertyNoSave(
             BackupFilesPresetEntry.SAVEDCONFIG, bfpe.toString());
     /*
-    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+    Cache.setPropertyNoSave(BackupFiles.ENABLED,
             Boolean.toString(enabled));
-    Cache.applicationProperties.setProperty(BackupFiles.SUFFIX, suffix);
-    Cache.applicationProperties.setProperty(BackupFiles.SUFFIX_DIGITS,
+    Cache.setPropertyNoSave(BackupFiles.SUFFIX, suffix);
+    Cache.setPropertyNoSave(BackupFiles.SUFFIX_DIGITS,
             Integer.toString(digits));
-    Cache.applicationProperties.setProperty(BackupFiles.REVERSE_ORDER,
+    Cache.setPropertyNoSave(BackupFiles.REVERSE_ORDER,
             Boolean.toString(reverse));
-    Cache.applicationProperties.setProperty(BackupFiles.NO_MAX,
+    Cache.setPropertyNoSave(BackupFiles.NO_MAX,
             Boolean.toString(noMax));
-    Cache.applicationProperties.setProperty(BackupFiles.ROLL_MAX,
+    Cache.setPropertyNoSave(BackupFiles.ROLL_MAX,
             Integer.toString(rollMax));
-    Cache.applicationProperties.setProperty(BackupFiles.CONFIRM_DELETE_OLD,
+    Cache.setPropertyNoSave(BackupFiles.CONFIRM_DELETE_OLD,
             "false");
             */
   }
index 3ca6ed8..59cb3cd 100644 (file)
@@ -184,7 +184,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
       {
         // retrieve dbref
 
-        SequenceFetcher sf = new SequenceFetcher(Desktop.instance,
+        SequenceFetcher sf = new SequenceFetcher(Desktop.getInstance(),
                 forSource, forAccession);
         sf.run();
         AlignFrame[] afs = Desktop.getAlignFrames();
@@ -211,7 +211,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
       }
       else
       {
-        Desktop.instance.closeAll_actionPerformed(null);
+        Desktop.getInstance().closeAll_actionPerformed(null);
         // recover stored project
         af = new FileLoader(false).LoadFileWaitTillLoaded(
                 savedProjects.get(first).toString(), DataSourceType.FILE);
@@ -278,7 +278,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
           }
           else
           {
-            Desktop.instance.closeAll_actionPerformed(null);
+            Desktop.getInstance().closeAll_actionPerformed(null);
             pass3 = 0;
             // recover stored project
             File storedProject = savedProjects.get(nextxref);
@@ -389,7 +389,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase
                 }
                 else
                 {
-                  Desktop.instance.closeAll_actionPerformed(null);
+                  Desktop.getInstance().closeAll_actionPerformed(null);
                   // recover stored project
                   File storedProject = savedProjects.get(nextnextxref);
                   if (storedProject == null)
index b753e94..7b3373c 100644 (file)
@@ -72,7 +72,7 @@ public class FeaturesFileTest
      * remove any sequence mappings created so they don't pollute other tests
      */
     StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(Desktop.instance);
+            .getStructureSelectionManager(Desktop.getInstance());
     ssm.resetAll();
   }
 
index 5bdebf3..faf05d3 100644 (file)
@@ -55,7 +55,7 @@ public class FileFormatsTest
   @Test(groups = "Functional")
   public void testGetReadableFormats()
   {
-    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3]";
+    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3, BSML]";
     FileFormats formats = FileFormats.getInstance();
     assertEquals(formats.getReadableFormats().toString(), expected);
   }
@@ -74,14 +74,15 @@ public class FileFormatsTest
   public void testDeregisterFileFormat()
   {
     String writable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, HMMER3]";
-    String readable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3]";
+    String readable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3, BSML]";
     FileFormats formats = FileFormats.getInstance();
+    System.out.println(formats.getReadableFormats().toString());
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
 
     formats.deregisterFileFormat(FileFormat.Fasta.getName());
     writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, HMMER3]";
-    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3]";
+    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3, BSML]";
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
 
@@ -90,7 +91,7 @@ public class FileFormatsTest
      */
     formats.registerFileFormat(FileFormat.Fasta);
     writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, HMMER3, Fasta]";
-    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3, Fasta]";
+    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, HMMER3, BSML, Fasta]";
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
   }
index fbdd782..3a0560c 100644 (file)
@@ -70,16 +70,16 @@ public class Jalview2xmlBase
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @BeforeTest(alwaysRun = true)
   public static void clearDesktop()
   {
-    if (Desktop.instance != null && Desktop.getFrames() != null
+    if (Desktop.getInstance() != null && Desktop.getFrames() != null
             && Desktop.getFrames().length > 0)
     {
-      Desktop.instance.closeAll_actionPerformed(null);
+      Desktop.getInstance().closeAll_actionPerformed(null);
     }
   }
 
index 06d177d..1614453 100644 (file)
@@ -63,7 +63,7 @@ public class JalviewExportPropertiesTests
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    jalview.gui.Desktop.getInstance().closeAll_actionPerformed(null);
 
   }
 
index 772ed2b..f3a7586 100644 (file)
@@ -69,15 +69,15 @@ public class SequenceAnnotationReportTest
     sar.appendFeature(sb, 2, null, sf, null, 0);
     assertEquals("123456", sb.toString());
 
-    // residuePos == 1 matches start of feature, text appended (but no <br/>)
+    // residuePos == 1 matches start of feature, text appended (but no <br>)
     // feature score is not included
     sar.appendFeature(sb, 1, null, sf, null, 0);
     assertEquals("123456disulfide bond 1:3", sb.toString());
 
     // residuePos == 3 matches end of feature, text appended
-    // <br/> is prefixed once sb.length() > 6
+    // <br> is prefixed once sb.length() > 6
     sar.appendFeature(sb, 3, null, sf, null, 0);
-    assertEquals("123456disulfide bond 1:3<br/>disulfide bond 1:3",
+    assertEquals("123456disulfide bond 1:3<br>disulfide bond 1:3",
             sb.toString());
   }
 
@@ -147,8 +147,8 @@ public class SequenceAnnotationReportTest
      */
     minmax.put("METAL", new float[][] { { 0f, 1f }, null });
     sar.appendFeature(sb, 1, fr, sf, null, 0);
-    // <br/> is appended to a buffer > 6 in length
-    assertEquals("METAL 1 3; Fe2-S<br/>METAL 1 3; Fe2-S Score=1.3",
+    // <br> is appended to a buffer > 6 in length
+    assertEquals("METAL 1 3; Fe2-S<br>METAL 1 3; Fe2-S Score=1.3",
             sb.toString());
 
     /*
@@ -299,7 +299,7 @@ public class SequenceAnnotationReportTest
             null));
     sb.setLength(0);
     sar.createSequenceAnnotationReport(sb, seq, true, true, null);
-    String expected = "<i>SeqDesc<br/>Type1 ; Nonpos Score=1.0</i>";
+    String expected = "<i>SeqDesc<br>Type1 ; Nonpos Score=1.0</i>";
     assertEquals(expected, sb.toString());
 
     /*
@@ -324,7 +324,7 @@ public class SequenceAnnotationReportTest
 
     sb.setLength(0);
     sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
-    expected = "<i>SeqDesc<br/>Metal ; Desc<br/>Type1 ; Nonpos</i>";
+    expected = "<i>SeqDesc<br>Metal ; Desc<br>Type1 ; Nonpos</i>";
     assertEquals(expected, sb.toString());
     
     /*
@@ -349,7 +349,7 @@ public class SequenceAnnotationReportTest
     seq.addSequenceFeature(sf2);
     sb.setLength(0);
     sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
-    expected = "<i>SeqDesc<br/>Metal ; Desc<br/>Type1 ; Nonpos<br/>Variant ; Havana</i>";
+    expected = "<i>SeqDesc<br>Metal ; Desc<br>Type1 ; Nonpos<br>Variant ; Havana</i>";
     assertEquals(expected, sb.toString());
 
     /*
@@ -370,13 +370,13 @@ public class SequenceAnnotationReportTest
     fc.setAttributeName("clinical_significance");
     fr.setColour("Variant", fc);
     sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
-    expected = "<i>SeqDesc<br/>UNIPROT P30419<br/>PDB 3iu1<br/>Metal ; Desc<br/>"
-            + "Type1 ; Nonpos<br/>Variant ; Havana; clinical_significance=benign</i>";
+    expected = "<i>SeqDesc<br>UNIPROT P30419<br>PDB 3iu1<br>Metal ; Desc<br>"
+            + "Type1 ; Nonpos<br>Variant ; Havana; clinical_significance=benign</i>";
     assertEquals(expected, sb.toString());
     // with showNonPositionalFeatures = false
     sb.setLength(0);
     sar.createSequenceAnnotationReport(sb, seq, true, false, fr);
-    expected = "<i>SeqDesc<br/>UNIPROT P30419<br/>PDB 3iu1</i>";
+    expected = "<i>SeqDesc<br>UNIPROT P30419<br>PDB 3iu1</i>";
     assertEquals(expected, sb.toString());
 
     /*
@@ -386,7 +386,7 @@ public class SequenceAnnotationReportTest
     sf2.setDescription(
             "This is a very long description which should be truncated");
     sar.createSequenceAnnotationReport(sb, seq, false, true, fr);
-    expected = "<i>SeqDesc<br/>Metal ; Desc<br/>Type1 ; Nonpos<br/>Variant ; This is a very long description which sh...; clinical_significance=benign</i>";
+    expected = "<i>SeqDesc<br>Metal ; Desc<br>Type1 ; Nonpos<br>Variant ; This is a very long description which sh...; clinical_significance=benign</i>";
     assertEquals(expected, sb.toString());
 
     // see other tests for treatment of status and html
@@ -421,10 +421,10 @@ public class SequenceAnnotationReportTest
     String report = sb.toString();
     assertTrue(report
             .startsWith(
-                    "<i><br/>UNIPROT P30410, P30411, P30412, P30413,...<br/>PDB0 3iu1"));
+                    "<i><br>UNIPROT P30410, P30411, P30412, P30413,...<br>PDB0 3iu1"));
     assertTrue(report
             .endsWith(
-                    "<br/>PDB7 3iu1<br/>PDB8,...<br/>(Output Sequence Details to list all database references)</i>"));
+                    "<br>PDB7 3iu1<br>PDB8,...<br>(Output Sequence Details to list all database references)</i>"));
   }
 
   /**
index b72852f..f0fbfc4 100644 (file)
@@ -39,6 +39,7 @@ import javax.swing.JInternalFrame;
 
 import org.testng.Assert;
 import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -96,6 +97,11 @@ import junit.extensions.PA;
 @Test(singleThreaded = true)
 public class Jalview2xmlTests extends Jalview2xmlBase
 {
+  @AfterMethod(alwaysRun = true)
+  public void tearDown()
+  {
+    Desktop.getInstance().closeAll_actionPerformed(null);
+  }
 
   @Override
   @BeforeClass(alwaysRun = true)
@@ -278,9 +284,9 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
     assertNotNull(af, "Didn't read in the example file correctly.");
-    assertTrue(Desktop.getAlignFrames().length == 1 + origCount,
+    assertEquals(Desktop.getAlignFrames().length,
+            1 + origCount,
             "Didn't gather the views in the example file.");
-
   }
 
   /**
@@ -412,7 +418,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" }, enabled = true)
   public void testStoreAndRecoverExpandedviews() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
 
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
@@ -440,7 +446,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the expanded view state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -466,7 +472,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverReferenceSeqSettings() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
     assertNotNull(af, "Didn't read in the example file correctly.");
@@ -505,7 +511,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the expanded view state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -599,7 +605,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverGroupRepSeqs() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     assertNotNull(af, "Didn't read in the example file correctly.");
@@ -674,7 +680,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the expanded view state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -719,7 +725,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverPDBEntry() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     String exampleFile = "examples/3W5V.pdb";
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
             DataSourceType.FILE);
@@ -768,7 +774,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     if (Desktop.getAlignFrames() != null)
     {
       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
@@ -819,7 +825,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverColourThresholds() throws IOException
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
 
@@ -882,7 +888,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
             ".jvp");
     tfile.deleteOnExit();
     new Jalview2XML(false).saveState(tfile);
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
             DataSourceType.FILE);
     Assert.assertNotNull(af, "Failed to reload project");
@@ -1077,7 +1083,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverHmmProfile() throws IOException
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
   
@@ -1107,7 +1113,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
             ".jvp");
     tfile.deleteOnExit();
     new Jalview2XML(false).saveState(tfile);
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
             DataSourceType.FILE);
     Assert.assertNotNull(af, "Failed to reload project");
@@ -1144,7 +1150,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testMergeDatasetsforManyViews() throws IOException
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
 
     // complex project - one dataset, several views on several alignments
     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
@@ -1188,7 +1194,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = "Functional")
   public void testPcaViewAssociation() throws IOException
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     final String PCAVIEWNAME = "With PCA";
     // create a new tempfile
     File tempfile = File.createTempFile("jvPCAviewAssoc", "jvp");
@@ -1222,10 +1228,10 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     }
 
     // load again.
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             tempfile.getCanonicalPath(), DataSourceType.FILE);
-    JInternalFrame[] frames = Desktop.instance.getAllFrames();
+    JInternalFrame[] frames = Desktop.getInstance().getAllFrames();
     // PCA and the tabbed alignment view should be the only two windows on the
     // desktop
     assertEquals(frames.length, 2,
@@ -1255,7 +1261,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
   @Test(groups = { "Functional" })
   public void testStoreAndRecoverGeneLocus() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     String seqData = ">P30419\nACDE\n>X1235\nGCCTGTGACGAA";
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
             DataSourceType.PASTE);
@@ -1291,7 +1297,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     {
       Assert.fail("Didn't save the state", e);
     }
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   
     new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
             DataSourceType.FILE);
index d82d847..fd90999 100644 (file)
@@ -6,24 +6,24 @@ import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
-import jalview.api.AlignViewportI;
+import java.awt.Color;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
 import jalview.api.FeatureColourI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
+import jalview.api.AlignViewportI;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 import jalview.schemes.FeatureColour;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 
-import java.awt.Color;
-import java.util.List;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.BeforeTest;
-import org.testng.annotations.Test;
-
 /**
  * Unit tests for feature colour determination, including but not limited to
  * <ul>
@@ -53,7 +53,7 @@ public class FeatureColourFinderTest
 
   private FeatureRendererModel fr;
 
-  @BeforeTest(alwaysRun = true)
+  @BeforeClass(alwaysRun = true)
   public void setUp()
   {
     // aligned column 8 is sequence position 6
index 2e04ecb..679ba6b 100644 (file)
@@ -27,6 +27,16 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
 import jalview.analysis.GeneticCodes;
 import jalview.api.AlignViewportI;
 import jalview.api.FeatureColourI;
@@ -46,17 +56,13 @@ import jalview.schemes.FeatureColour;
 import jalview.util.matcher.Condition;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
 
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.testng.annotations.Test;
-
 public class FeatureRendererTest
 {
+  @AfterMethod(alwaysRun = true)
+  public void tearDown()
+  {
+    Desktop.getInstance().closeAll_actionPerformed(null);
+  }
 
   @Test(groups = "Functional")
   public void testFindAllFeatures()
index 5db3743..06be31e 100644 (file)
@@ -172,7 +172,7 @@ public class ColourSchemesTest
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
   }
 
   @Test(groups = "Functional")
index 4bee3f5..40715c8 100644 (file)
@@ -75,7 +75,8 @@ public class Mapping
       int coils[] = { 266, 275, 278, 287, 289, 298, 302, 316 }, helices[] = new int[]
       { 303, 315 }, sheets[] = new int[] { 267, 268, 269, 270 };
 
-      StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+      StructureSelectionManager ssm = StructureSelectionManager
+              .getStructureSelectionManager(null);
       StructureFile pmap = ssm.setMapping(true, new SequenceI[] { uprot },
               new String[] { "A" }, "test/jalview/ext/jmol/1QCF.pdb",
               DataSourceType.FILE);
@@ -144,7 +145,8 @@ public class Mapping
             "EIVKGVCSNFLCDLQPGDNVQITGPVGKEMLMPKDPNATIIMLATGTGIAPFRSFLWKMFFEKHDDYKFNGLGWLFLGVPTSSSLLYKEEFGKM");
     Sequence sq1 = new Sequence(sq);
     String inFile;
-    StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(null);
     // Associate the 1GAQ pdb file with the subsequence 'imported' from another
     // source
     StructureFile pde = ssm.setMapping(true, new SequenceI[] { sq },
@@ -243,7 +245,8 @@ public class Mapping
                     ">FER1_MAIZE/1-150 Ferredoxin-1, chloroplast precursor\nMATVLGSPRAPAFFFSSSSLRAAPAPTAVALPAAKVGIMGRSASSRRRLRAQATYNVKLITPEGEVELQVPD\nDVYILDQAEEDGIDLPYSCRAGSCSSCAGKVVSGSVDQSDQSYLDDGQIADGWVLTCHAYPTSDVVIETHKE\nEELTGA",
                     DataSourceType.PASTE, FileFormat.Fasta);
     SequenceI newseq = seqf.getViewport().getAlignment().getSequenceAt(0);
-    StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(null);
     StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
             new String[] { null }, "examples/3W5V.pdb",
             DataSourceType.FILE);
@@ -272,7 +275,8 @@ public class Mapping
     // make it harder by shifting the copy vs the reference
     newseq.setStart(refseq.getStart() + 25);
     newseq.setEnd(refseq.getLength() + 25 + refseq.getStart());
-    StructureSelectionManager ssm = new jalview.structure.StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(null);
     ssm.setProcessSecondaryStructure(true);
     ssm.setAddTempFacAnnot(true);
     StructureFile pmap = ssm.setMapping(true, new SequenceI[] { newseq },
index e59648f..044cbc7 100644 (file)
@@ -80,7 +80,8 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   public void setUp()
   {
     StructureImportSettings.setShowSeqFeatures(true);
-    ssm = new StructureSelectionManager();
+    ssm = StructureSelectionManager.getStructureSelectionManager(null);
+    ssm.resetAll();
   }
 
   @Test(groups = { "Functional" })
@@ -139,10 +140,11 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     ssm.registerMappings(set2);
     ssm.registerMappings(set2);
 
-    assertEquals(3, ssm.getSequenceMappings().size());
-    assertTrue(ssm.getSequenceMappings().contains(acf1));
-    assertTrue(ssm.getSequenceMappings().contains(acf2));
-    assertTrue(ssm.getSequenceMappings().contains(acf3));
+    List<AlignedCodonFrame> mappings = ssm.getSequenceMappings();
+    assertEquals(3, mappings.size());
+    assertTrue(mappings.contains(acf1));
+    assertTrue(mappings.contains(acf2));
+    assertTrue(mappings.contains(acf3));
   }
 
   /**
@@ -155,7 +157,7 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
     SequenceI seq = new Sequence(
             "1GAQ|B",
             "ATYNVKLITPEGEVELQVPDDVYILDQAEEDGIDLPYSCRAGSCSSCAGKVVSGSVDQSDQSYLDDGQIADGWVLTCHAYPTSDVVIETHKEEELTGA");
-    StructureSelectionManager sm = new StructureSelectionManager();
+    StructureSelectionManager sm = StructureSelectionManager.getStructureSelectionManager(null);
     sm.setProcessSecondaryStructure(true);
     sm.setAddTempFacAnnot(true);
     StructureFile pmap = sm.setMapping(true, new SequenceI[] { seq },
@@ -199,14 +201,14 @@ public class StructureSelectionManagerTest extends Jalview2xmlBase
   {
     // for some reason 'BeforeMethod' (which should be inherited from
     // Jalview2XmlBase isn't always called)...
-    Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.getInstance().closeAll_actionPerformed(null);
     try { 
       Thread.sleep(200);
     } catch (Exception foo) {}; 
     SequenceI seq = new Sequence("4IM2|A",
             "LDFCIRNIEKTVMGEISDIHTKLLRLSSSQGTIE");
     String P4IM2_MISSING = "examples/testdata/4IM2_missing.pdb";
-    StructureSelectionManager sm = new StructureSelectionManager();
+    StructureSelectionManager sm = StructureSelectionManager.getStructureSelectionManager(null);
     sm.setProcessSecondaryStructure(true);
     sm.setAddTempFacAnnot(true);
     StructureFile pmap = sm.setMapping(true, new SequenceI[] { seq },
index af02d5e..ac7e736 100644 (file)
@@ -130,7 +130,7 @@ public class AAStructureBindingModelTest
     PDBEntry importedPDB = new PDBEntry("3A6S", "", Type.PDB,
             "Paste");
     AAStructureBindingModel binder = new AAStructureBindingModel(
-            new StructureSelectionManager(), new PDBEntry[]
+            StructureSelectionManager.getStructureSelectionManager(null), new PDBEntry[]
             { importedPDB },
             new SequenceI[][]
             { importedAl.getSequencesArray() }, null)
@@ -272,7 +272,7 @@ public class AAStructureBindingModelTest
     seqs[0] = new SequenceI[] { seq1a, seq1b };
     seqs[1] = new SequenceI[] { seq2 };
     seqs[2] = new SequenceI[] { seq3 };
-    StructureSelectionManager ssm = new StructureSelectionManager();
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(null);
 
     ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1,
             DataSourceType.PASTE, null);
index fa4091f..9d9482c 100644 (file)
@@ -24,13 +24,13 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 
-import jalview.gui.JvOptionPane;
-
 import java.awt.Color;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import jalview.gui.JvOptionPane;
+
 public class ColorUtilsTest
 {
 
@@ -206,7 +206,7 @@ public class ColorUtilsTest
      */
     assertNull(ColorUtils.parseColourString(null));
     assertNull(ColorUtils.parseColourString("rubbish"));
-    assertEquals(Color.WHITE, ColorUtils.parseColourString("-1"));
+    assertNull(ColorUtils.parseColourString("-1"));
     assertNull(ColorUtils.parseColourString(String
             .valueOf(Integer.MAX_VALUE)));
     assertNull(ColorUtils.parseColourString("100,200,300")); // out of range
index d2c48f1..49f1335 100644 (file)
@@ -32,6 +32,7 @@ import java.awt.Toolkit;
 import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
 
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -141,4 +142,17 @@ public class PlatformTest
     assertEquals(Platform.escapeBackslashes("hello world"), "hello world");
     assertEquals(Platform.escapeBackslashes("hello\\world"), "hello\\\\world");
   }
+
+  @Test(groups = { "Functional" })
+  public void getLeadingIntegerFromString()
+  {
+    Assert.assertEquals(Platform.getLeadingIntegerValue("1234abcd", -1),
+            1234);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("1234", -1), 1234);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("abcd", -1), -1);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("abcd1234", -1),
+            -1);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("None", -1), -1);
+    Assert.assertEquals(Platform.getLeadingIntegerValue("Null", -1), -1);
+  }
 }
index d1e32b9..b14b3b5 100644 (file)
@@ -57,9 +57,9 @@ public class PDBSequenceFetcherTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // ensure 'add annotation from structure' is selected
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
 
     sf = new SequenceFetcher();
@@ -76,7 +76,7 @@ public class PDBSequenceFetcherTest
   @Test(groups = { "Network" }, enabled = true)
   public void testRnaSeqRetrieve() throws Exception
   {
-    Cache.applicationProperties.setProperty("PDB_DOWNLOAD_FORMAT", "PDB");
+    Cache.setPropertyNoSave("PDB_DOWNLOAD_FORMAT", "PDB");
     List<DbSourceProxy> sps = sf.getSourceProxy("PDB");
     AlignmentI response = sps.get(0).getSequenceRecords("2GIS");
     assertTrue(response != null);
@@ -149,7 +149,7 @@ public class PDBSequenceFetcherTest
         // chains in structure have a mapping to data in the structure
         List<SequenceFeature> prev = null;
         int lastp = -1;
-        for (int col = 1; col <= sq.getLength(); col++)
+        for (int col = 1, ns = sq.getLength(); col <= ns; col++)
         {
           List<SequenceFeature> sf = sq.findFeatures(col, col, "RESNUM");
           if (sf.size() != 1)
index 629bd8a..9d40624 100644 (file)
@@ -46,9 +46,9 @@ public class RemoteFormatTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // ensure 'add annotation from structure' is selected
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
 
     sf = new SequenceFetcher();
index 12f5e1b..9c291c3 100644 (file)
@@ -85,7 +85,7 @@ public class JalviewJabawsTestUtils
   public static Jws2Discoverer getJabawsDiscoverer(boolean localhost)
   {
     jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
-            .getDiscoverer();
+            .getInstance();
     String svcurls = "";
     if (localhost)
     {
@@ -97,7 +97,7 @@ public class JalviewJabawsTestUtils
         services.add(url);
       }
       ;
-      Jws2Discoverer.getDiscoverer().setServiceUrls(services);
+      Jws2Discoverer.getInstance().setServiceUrls(services);
     }
     try
     {
index 6122d6d..c0f9b66 100644 (file)
@@ -23,17 +23,6 @@ package jalview.ws.sifts;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
-import jalview.api.DBRefEntryI;
-import jalview.bin.Cache;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.gui.JvOptionPane;
-import jalview.io.DataSourceType;
-import jalview.structure.StructureMapping;
-import jalview.xml.binding.sifts.Entry.Entity;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -48,6 +37,16 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
+import jalview.api.DBRefEntryI;
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.structure.StructureMapping;
+import jalview.xml.binding.sifts.Entry.Entity;
 import mc_view.Atom;
 import mc_view.PDBfile;
 
@@ -189,7 +188,6 @@ public class SiftsClientTest
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     // SIFTs entries are updated weekly - so use saved SIFTs file to enforce
     // test reproducibility
-    new SiftsSettings();
     SiftsSettings.setSiftDownloadDirectory(jalview.bin.Cache.getDefault(
             "sifts_download_dir", DEFAULT_SIFTS_DOWNLOAD_DIR));
     SiftsSettings.setMapWithSifts(true);
@@ -311,7 +309,7 @@ public class SiftsClientTest
     atom.atomIndex = 7;
     atoms.add(atom);
     int actualAtomIndex = siftsClient.getAtomIndex(1, atoms);
-    Assert.assertEquals(actualAtomIndex, siftsClient.UNASSIGNED);
+    Assert.assertEquals(actualAtomIndex, SiftsClient.UNASSIGNED);
     actualAtomIndex = siftsClient.getAtomIndex(43, atoms);
     Assert.assertEquals(actualAtomIndex, 7);
   }
@@ -514,22 +512,4 @@ groups = { "Network" },
     Assert.assertNull(entityP);
 
   }
-
-  @Test(groups = { "Network" })
-  public void getLeadingIntegerFromString()
-  {
-    Assert.assertEquals(
-            SiftsClient.getLeadingIntegerValue("1234abcd", -1), 1234);
-    Assert.assertEquals(
-            SiftsClient.getLeadingIntegerValue("1234", -1),
-            1234);
-    Assert.assertEquals(
-            SiftsClient.getLeadingIntegerValue("abcd", -1), -1);
-    Assert.assertEquals(
-            SiftsClient.getLeadingIntegerValue("abcd1234", -1), -1);
-    Assert.assertEquals(
-            SiftsClient.getLeadingIntegerValue("None", -1), -1);
-    Assert.assertEquals(
-            SiftsClient.getLeadingIntegerValue("Null", -1), -1);
-  }
 }
index 2bd8dd0..fe70a98 100644 (file)
@@ -35,7 +35,8 @@ public class UrlDownloadClientTest {
   public void UrlDownloadTest()
   {
     UrlDownloadClient client = new UrlDownloadClient();
-    String urlstring = "http://identifiers.org/rest/collections/";
+    String urlstring = "http://www.jalview.org/services/identifiers";
+    // was "http://identifiers.org/rest/collections/";
     String outfile = "testfile.tmp";
 
     try
@@ -58,12 +59,14 @@ public class UrlDownloadClientTest {
 
     // download file has a believable size
     // identifiers.org file typically at least 250K
-    Assert.assertTrue(f.length() > 250000);
-
+    long n = f.length();
     if (f.exists())
     {
       f.delete();
     }
+    // 74589
+    Assert.assertTrue(n > 70000);
+
 
   }
 
index c99d185..0e34a24 100644 (file)
@@ -325,11 +325,11 @@ public class PDBfileTest
   public void setUp()
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("STRUCT_FROM_PDB",
+    Cache.setPropertyNoSave("STRUCT_FROM_PDB",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_TEMPFACT_ANN",
+    Cache.setPropertyNoSave("ADD_TEMPFACT_ANN",
             Boolean.TRUE.toString());
-    Cache.applicationProperties.setProperty("ADD_SS_ANN",
+    Cache.setPropertyNoSave("ADD_SS_ANN",
             Boolean.TRUE.toString());
     StructureImportSettings.setDefaultStructureFileFormat("PDB");
   }
diff --git a/unused/JalviewAppLoader.java b/unused/JalviewAppLoader.java
new file mode 100644 (file)
index 0000000..0f4833c
--- /dev/null
@@ -0,0 +1,27 @@
+package jalview.bin;
+
+import java.io.File;
+import java.net.URISyntaxException;
+
+import jalview.gui.AlignFrame;
+import jalview.io.BioJsHTMLOutput;
+import jalview.io.FileFormatI;
+import jalview.io.HtmlSvgOutput;
+import jalview.util.Platform;
+
+/**
+ * Abandoned -- see JalviewApp
+ * 
+ * A class to load parameters for either JalviewLite or Jalview
+ * 
+ * @author hansonr
+ *
+ */
+public class JalviewAppLoader
+{
+  
+  public JalviewAppLoader()
+  {
+  }
+
+}
\ No newline at end of file
diff --git a/unused/JalviewJSApp.java b/unused/JalviewJSApp.java
new file mode 100644 (file)
index 0000000..654569d
--- /dev/null
@@ -0,0 +1,12 @@
+package jalview.bin;
+
+import java.util.List;
+
+import jalview.appletgui.js.JSFunctionExec;
+
+public interface JalviewJSApp
+{
+
+
+
+}
diff --git a/utils/clover/lib/clover-ant-4.4.1.jar b/utils/clover/lib/clover-ant-4.4.1.jar
new file mode 100644 (file)
index 0000000..4f92395
Binary files /dev/null and b/utils/clover/lib/clover-ant-4.4.1.jar differ
diff --git a/utils/cmd-nox.sh b/utils/cmd-nox.sh
new file mode 100755 (executable)
index 0000000..bd615bd
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+CMD=$(basename $0)
+CMD=${CMD%-nox.sh}
+
+echo "Running '$CMD' headlessly"
+
+xvfb-run -s "-screen 0 1280x800x16" -e /dev/stdout -a $CMD ${@}
similarity index 100%
rename from doc/github.css
rename to utils/doc/github.css
diff --git a/utils/gradle-nox.sh b/utils/gradle-nox.sh
new file mode 120000 (symlink)
index 0000000..4abb9a2
--- /dev/null
@@ -0,0 +1 @@
+cmd-nox.sh
\ No newline at end of file
diff --git a/utils/jalviewjs/_j2sclasslist.txt b/utils/jalviewjs/_j2sclasslist.txt
deleted file mode 100644 (file)
index e35111e..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-java/applet/Applet.js
-java/applet/AppletContext.js
-java/applet/AppletStub.js
-java/applet/JSApplet.js
-java/awt/ActiveEvent.js
-java/awt/Adjustable.js
-java/awt/AWTEvent.js
-java/awt/AWTEventMulticaster.js
-java/awt/AWTKeyStroke.js
-java/awt/BasicStroke.js
-java/awt/BorderLayout.js
-java/awt/Button.js
-java/awt/Color.js
-java/awt/color/ColorSpace.js
-java/awt/Component.js
-java/awt/ComponentOrientation.js
-java/awt/ContainerOrderFocusTraversalPolicy.js
-java/awt/Container.js
-java/awt/Cursor.js
-java/awt/DefaultFocusTraversalPolicy.js
-java/awt/DefaultKeyboardFocusManager.js
-java/awt/Dialog.js
-java/awt/Dimension.js
-java/awt/dnd/peer/DropTargetPeer.js
-java/awt/event/ActionListener.js
-java/awt/event/AdjustmentEvent.js
-java/awt/event/AdjustmentListener.js
-java/awt/event/AWTEventListener.js
-java/awt/event/ComponentAdapter.js
-java/awt/event/ComponentEvent.js
-java/awt/event/ComponentListener.js
-java/awt/event/ContainerListener.js
-java/awt/event/FocusEvent.js
-java/awt/event/FocusListener.js
-java/awt/event/HierarchyBoundsListener.js
-java/awt/event/HierarchyListener.js
-java/awt/event/InputEvent.js
-java/awt/event/InputMethodListener.js
-java/awt/event/InvocationEvent.js
-java/awt/event/ItemEvent.js
-java/awt/event/ItemListener.js
-java/awt/event/KeyListener.js
-java/awt/event/MouseEvent.js
-java/awt/event/MouseListener.js
-java/awt/event/MouseMotionListener.js
-java/awt/event/MouseWheelListener.js
-java/awt/event/TextListener.js
-java/awt/event/WindowAdapter.js
-java/awt/event/WindowEvent.js
-java/awt/event/WindowFocusListener.js
-java/awt/event/WindowListener.js
-java/awt/event/WindowStateListener.js
-java/awt/EventDispatchThread.js
-java/awt/EventFilter.js
-java/awt/EventQueue.js
-java/awt/EventQueueItem.js
-java/awt/FlowLayout.js
-java/awt/FocusTraversalPolicy.js
-java/awt/Font.js
-java/awt/font/FontRenderContext.js
-java/awt/FontMetrics.js
-java/awt/Frame.js
-java/awt/geom/AffineTransform.js
-java/awt/geom/Dimension2D.js
-java/awt/geom/Path2D.js
-java/awt/geom/PathIterator.js
-java/awt/geom/Point2D.js
-java/awt/geom/Rectangle2D.js
-java/awt/geom/RectangularShape.js
-java/awt/geom/RectIterator.js
-java/awt/GraphicsCallback.js
-java/awt/GraphicsConfiguration.js
-java/awt/GraphicsDevice.js
-java/awt/GraphicsEnvironment.js
-java/awt/image/ImageObserver.js
-java/awt/Insets.js
-java/awt/ItemSelectable.js
-java/awt/JSComponent.js
-java/awt/JSDialog.js
-java/awt/JSFrame.js
-java/awt/JSPanel.js
-java/awt/KeyboardFocusManager.js
-java/awt/KeyEventDispatcher.js
-java/awt/KeyEventPostProcessor.js
-java/awt/Label.js
-java/awt/LayoutManager.js
-java/awt/LayoutManager2.js
-java/awt/LightweightDispatcher.js
-java/awt/Paint.js
-java/awt/Panel.js
-java/awt/peer/ComponentPeer.js
-java/awt/peer/ContainerPeer.js
-java/awt/peer/FramePeer.js
-java/awt/peer/KeyboardFocusManagerPeer.js
-java/awt/peer/LightweightPeer.js
-java/awt/peer/WindowPeer.js
-java/awt/Point.js
-java/awt/Queue.js
-java/awt/Rectangle.js
-java/awt/RenderingHints.js
-java/awt/Scrollbar.js
-java/awt/ScrollPane.js
-java/awt/Shape.js
-java/awt/Stroke.js
-java/awt/TextArea.js
-java/awt/TextComponent.js
-java/awt/TextField.js
-java/awt/Toolkit.js
-java/awt/Transparency.js
-java/awt/Window.js
-java/beans/ChangeListenerMap.js
-java/beans/PropertyChangeEvent.js
-java/beans/PropertyChangeListener.js
-java/beans/PropertyChangeSupport.js
-java/lang/AbstractStringBuilder.js
-java/lang/Class.js
-java/lang/Enum.js
-java/lang/Iterable.js
-java/lang/reflect/Constructor.js
-java/lang/reflect/Method.js
-java/lang/StringBuffer.js
-java/lang/StringBuilder.js
-java/lang/Thread.js
-java/lang/ThreadGroup.js
-java/math/RoundingMode.js
-java/net/URL.js
-java/net/URLStreamHandlerFactory.js
-java/text/CharacterIterator.js
-java/text/DecimalFormat.js
-java/text/DecimalFormatSymbols.js
-java/text/DigitList.js
-java/text/FieldPosition.js
-java/text/Format.js
-java/text/NumberFormat.js
-java/util/AbstractCollection.js
-java/util/AbstractList.js
-java/util/AbstractMap.js
-java/util/AbstractSequentialList.js
-java/util/AbstractSet.js
-java/util/ArrayList.js
-java/util/Arrays.js
-java/util/Collection.js
-java/util/Collections.js
-java/util/Comparator.js
-java/util/Deque.js
-java/util/Dictionary.js
-java/util/Enumeration.js
-java/util/EventListener.js
-java/util/EventObject.js
-java/util/HashMap.js
-java/util/HashSet.js
-java/util/Hashtable.js
-java/util/IdentityHashMap.js
-java/util/Iterator.js
-java/util/LinkedHashMap.js
-java/util/LinkedList.js
-java/util/List.js
-java/util/ListResourceBundle.js
-java/util/Locale.js
-java/util/Map.js
-java/util/Objects.js
-java/util/Queue.js
-java/util/Random.js
-java/util/RandomAccess.js
-java/util/ResourceBundle.js
-java/util/Set.js
-java/util/TimSort.js
-java/util/Vector.js
-javajs/api/JSFunction.js
-javajs/util/AjaxURLConnection.js
-javajs/util/AjaxURLStreamHandlerFactory.js
-javajs/util/AU.js
-javajs/util/JSThread.js
-javajs/util/Lst.js
-javajs/util/PT.js
-javajs/util/SB.js
-javax/net/ssl/HttpsUrlConnection.js
-javax/swing/AbstractAction.js
-javax/swing/AbstractButton.js
-javax/swing/AbstractListModel.js
-javax/swing/Action.js
-javax/swing/ActionMap.js
-javax/swing/AncestorNotifier.js
-javax/swing/ArrayTable.js
-javax/swing/border/AbstractBorder.js
-javax/swing/border/BevelBorder.js
-javax/swing/border/Border.js
-javax/swing/border/CompoundBorder.js
-javax/swing/border/EmptyBorder.js
-javax/swing/border/EtchedBorder.js
-javax/swing/border/LineBorder.js
-javax/swing/BorderFactory.js
-javax/swing/BoundedRangeModel.js
-javax/swing/BoxLayout.js
-javax/swing/ButtonGroup.js
-javax/swing/ButtonModel.js
-javax/swing/ClientPropertyKey.js
-javax/swing/ComboBoxModel.js
-javax/swing/DefaultBoundedRangeModel.js
-javax/swing/DefaultButtonModel.js
-javax/swing/DefaultComboBoxModel.js
-javax/swing/DefaultSingleSelectionModel.js
-javax/swing/DropMode.js
-javax/swing/event/AncestorEvent.js
-javax/swing/event/AncestorListener.js
-javax/swing/event/CaretEvent.js
-javax/swing/event/CaretListener.js
-javax/swing/event/ChangeEvent.js
-javax/swing/event/ChangeListener.js
-javax/swing/event/DocumentEvent.js
-javax/swing/event/DocumentListener.js
-javax/swing/event/EventListenerList.js
-javax/swing/event/ListDataEvent.js
-javax/swing/event/ListDataListener.js
-javax/swing/event/UndoableEditEvent.js
-javax/swing/event/UndoableEditListener.js
-javax/swing/FocusManager.js
-javax/swing/InternalFrameFocusTraversalPolicy.js
-javax/swing/LayoutComparator.js
-javax/swing/LayoutFocusTraversalPolicy.js
-javax/swing/SortingFocusTraversalPolicy.js
-javax/swing/SwingContainerOrderFocusTraversalPolicy.js
-javax/swing/SwingDefaultFocusTraversalPolicy.js
-javax/swing/InputMap.js
-javax/swing/JApplet.js
-javax/swing/JButton.js
-javax/swing/JCheckBox.js
-javax/swing/JCheckBoxMenuItem.js
-javax/swing/JComboBox.js
-javax/swing/JComponent.js
-javax/swing/JFrame.js
-javax/swing/JLabel.js
-javax/swing/JLayeredPane.js
-javax/swing/JMenu.js
-javax/swing/JMenuBar.js
-javax/swing/JMenuItem.js
-javax/swing/JPanel.js
-javax/swing/JPopupMenu.js
-javax/swing/JRadioButtonMenuItem.js
-javax/swing/JRootPane.js
-javax/swing/JScrollBar.js
-javax/swing/JScrollPane.js
-javax/swing/JSeparator.js
-javax/swing/JTextArea.js
-javax/swing/JTextField.js
-javax/swing/JToggleButton.js
-javax/swing/JViewport.js
-javax/swing/KeyboardManager.js
-javax/swing/KeyStroke.js
-javax/swing/ListModel.js
-javax/swing/LookAndFeel.js
-javax/swing/MenuElement.js
-javax/swing/MutableComboBoxModel.js
-javax/swing/plaf/ActionMapUIResource.js
-javax/swing/plaf/basic/BasicBorders.js
-javax/swing/plaf/BorderUIResource.js
-javax/swing/plaf/ColorUIResource.js
-javax/swing/plaf/ComponentUI.js
-javax/swing/plaf/DimensionUIResource.js
-javax/swing/plaf/FontUIResource.js
-javax/swing/plaf/InputMapUIResource.js
-javax/swing/plaf/InsetsUIResource.js
-javax/swing/plaf/UIResource.js
-javax/swing/RepaintManager.js
-javax/swing/RootPaneContainer.js
-javax/swing/Scrollable.js
-javax/swing/ScrollPaneConstants.js
-javax/swing/ScrollPaneLayout.js
-javax/swing/SingleSelectionModel.js
-javax/swing/SizeRequirements.js
-javax/swing/SwingConstants.js
-javax/swing/SwingPaintEventDispatcher.js
-javax/swing/SwingUtilities.js
-javax/swing/text/AbstractDocument.js
-javax/swing/text/AttributeSet.js
-javax/swing/text/Caret.js
-javax/swing/text/DefaultCaret.js
-javax/swing/text/DefaultEditorKit.js
-javax/swing/text/Document.js
-javax/swing/text/EditorKit.js
-javax/swing/text/Element.js
-javax/swing/text/GapContent.js
-javax/swing/text/GapVector.js
-javax/swing/text/JTextComponent.js
-javax/swing/text/MutableAttributeSet.js
-javax/swing/text/PlainDocument.js
-javax/swing/text/PlainView.js
-javax/swing/text/Position.js
-javax/swing/text/Segment.js
-javax/swing/text/SegmentCache.js
-javax/swing/text/SimpleAttributeSet.js
-javax/swing/text/Style.js
-javax/swing/text/StyleConstants.js
-javax/swing/text/StyleContext.js
-javax/swing/text/TabExpander.js
-javax/swing/text/TextAction.js
-javax/swing/text/Utilities.js
-javax/swing/text/View.js
-javax/swing/tree/TreeNode.js
-javax/swing/UIDefaults.js
-javax/swing/UIManager.js
-javax/swing/undo/AbstractUndoableEdit.js
-javax/swing/undo/CompoundEdit.js
-javax/swing/undo/UndoableEdit.js
-javax/swing/ViewportLayout.js
-javax/swing/WindowConstants.js
-sun/awt/AppContext.js
-sun/awt/AWTAutoShutdown.js
-sun/awt/CausedFocusEvent.js
-sun/awt/ComponentFactory.js
-sun/awt/KeyboardFocusManagerPeerProvider.js
-sun/awt/MostRecentKeyValue.js
-sun/awt/MostRecentThreadAppContext.js
-sun/awt/PaintEventDispatcher.js
-sun/awt/PostEventQueue.js
-sun/awt/RequestFocusController.js
-sun/awt/SunToolkit.js
-sun/awt/WindowClosingListener.js
-sun/awt/WindowClosingSupport.js
-sun/font/FontDesignMetrics.js
-sun/swing/DefaultLookup.js
-sun/swing/SwingLazyValue.js
-sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
-sun/util/resources/LocaleData.js
-swingjs/a2s/A2SContainer.js
-swingjs/a2s/A2SEvent.js
-swingjs/a2s/A2SListener.js
-swingjs/a2s/Applet.js
-swingjs/a2s/Button.js
-swingjs/a2s/Label.js
-swingjs/a2s/Panel.js
-swingjs/a2s/Scrollbar.js
-swingjs/a2s/ScrollPane.js
-swingjs/a2s/TextArea.js
-swingjs/a2s/TextField.js
-swingjs/api/Interface.js
-swingjs/api/js/DOMNode.js
-swingjs/api/js/HTML5CanvasContext2D.js
-swingjs/api/js/JSInterface.js
-swingjs/jquery/JQueryUI.js
-swingjs/JSApp.js
-swingjs/JSAppletThread.js
-swingjs/JSAppletViewer.js
-swingjs/JSFocusPeer.js
-swingjs/JSFontMetrics.js
-swingjs/JSFrameViewer.js
-swingjs/JSGraphics2D.js
-swingjs/JSGraphicsConfiguration.js
-swingjs/JSGraphicsEnvironment.js
-swingjs/JSMouse.js
-swingjs/JSNullComponentPeer.js
-swingjs/JSScreenDevice.js
-swingjs/JSThreadGroup.js
-swingjs/JSToolkit.js
-swingjs/JSUtil.js
-swingjs/plaf/ButtonListener.js
-swingjs/plaf/DefaultMenuLayout.js
-swingjs/plaf/HTML5LookAndFeel.js
-swingjs/plaf/JSAppletUI.js
-swingjs/plaf/JSButtonUI.js
-swingjs/plaf/JSCheckBoxMenuItemUI.js
-swingjs/plaf/JSCheckBoxUI.js
-swingjs/plaf/JSComboBoxUI.js
-swingjs/plaf/JSComponentUI.js
-swingjs/plaf/JSEventHandler.js
-swingjs/plaf/JSFrameUI.js
-swingjs/plaf/JSGraphicsUtils.js
-swingjs/plaf/JSLabelUI.js
-swingjs/plaf/JSLayeredPaneUI.js
-swingjs/plaf/JSLightweightUI.js
-swingjs/plaf/JSMenuBarUI.js
-swingjs/plaf/JSMenuItemUI.js
-swingjs/plaf/JSMenuUI.js
-swingjs/plaf/JSPanelUI.js
-swingjs/plaf/JSPopupMenuSeparatorUI.js
-swingjs/plaf/JSPopupMenuUI.js
-swingjs/plaf/JSRadioButtonMenuItemUI.js
-swingjs/plaf/JSRadioButtonUI.js
-swingjs/plaf/JSRootPaneUI.js
-swingjs/plaf/JSScrollBarUI.js
-swingjs/plaf/JSScrollPaneUI.js
-swingjs/plaf/JSSeparatorUI.js
-swingjs/plaf/JSSliderUI.js
-swingjs/plaf/JSTextAreaUI.js
-swingjs/plaf/JSTextFieldUI.js
-swingjs/plaf/JSTextUI.js
-swingjs/plaf/JSTextViewUI.js
-swingjs/plaf/JSViewportUI.js
-swingjs/plaf/JSWindowUI.js
-swingjs/plaf/LazyActionMap.js
-swingjs/plaf/Resizer.js
-swingjs/plaf/TextListener.js
-
-
index 5d60006..e60fe60 100644 (file)
-jalview/analysis/AAFrequency.js
-jalview/analysis/AlignSeq.js
-jalview/analysis/AlignmentAnnotationUtils.js
-jalview/analysis/AlignmentUtils.js
-jalview/analysis/AnnotationSorter.js
-jalview/analysis/Conservation.js
-jalview/analysis/CrossRef.js
-jalview/analysis/NJTree.js
-jalview/analysis/SeqsetUtils.js
-jalview/analysis/TreeBuilder.js
-jalview/analysis/TreeModel.js
-jalview/analysis/scoremodels/DistanceScoreModel.js
-jalview/analysis/scoremodels/FeatureDistanceModel.js
-jalview/analysis/scoremodels/PIDModel.js
-jalview/analysis/scoremodels/ScoreMatrix.js
-jalview/analysis/scoremodels/ScoreModels.js
-jalview/analysis/scoremodels/SimilarityParams.js
-jalview/analysis/scoremodels/SimilarityScoreModel.js
-jalview/api/AlignCalcManagerI.js
-jalview/api/AlignCalcWorkerI.js
-jalview/api/AlignViewControllerGuiI.js
-jalview/api/AlignViewControllerI.js
-jalview/api/AlignViewportI.js
-jalview/api/AlignmentViewPanel.js
-jalview/api/BuildDetailsI.js
-jalview/api/FeatureColourI.js
-jalview/api/FeatureRenderer.js
-jalview/api/FeaturesDisplayedI.js
-jalview/api/OOMHandlerI.js
-jalview/api/SequenceRenderer.js
-jalview/api/StructureSelectionManagerProvider.js
-jalview/api/ViewStyleI.js
-jalview/api/analysis/PairwiseScoreModelI.js
-jalview/api/analysis/ScoreModelI.js
-jalview/api/analysis/SimilarityParamsI.js
-jalview/bin/ArgsParser.js
-jalview/bin/BuildDetails.js
-jalview/bin/Cache.js
-jalview/bin/Jalview.js
-jalview/bin/JalviewJS2.js
-jalview/controller/AlignViewController.js
-jalview/datamodel/ASequence.js
-jalview/datamodel/ASequenceI.js
-jalview/datamodel/Alignment.js
-jalview/datamodel/AlignmentAnnotation.js
-jalview/datamodel/AlignmentI.js
-jalview/datamodel/AlignmentView.js
-jalview/datamodel/AnnotatedCollectionI.js
-jalview/datamodel/Annotation.js
-jalview/datamodel/BinaryNode.js
-jalview/datamodel/CigarArray.js
-jalview/datamodel/CigarBase.js
-jalview/datamodel/CigarSimple.js
-jalview/datamodel/ColumnSelection.js
-jalview/datamodel/ContiguousI.js
-jalview/datamodel/DBRefSource.js
-jalview/datamodel/HiddenColumns.js
-jalview/datamodel/HiddenColumnsCursor.js
-jalview/datamodel/HiddenCursorPosition.js
-jalview/datamodel/HiddenSequences.js
-jalview/datamodel/PDBEntry.js
-jalview/datamodel/Profile.js
-jalview/datamodel/ProfileI.js
-jalview/datamodel/Profiles.js
-jalview/datamodel/ProfilesI.js
-jalview/datamodel/Range.js
-jalview/datamodel/RangeIterator.js
-jalview/datamodel/ResidueCount.js
-jalview/datamodel/SearchResults.js
-jalview/datamodel/SearchResultsI.js
-jalview/datamodel/SeqCigar.js
-jalview/datamodel/Sequence.js
-jalview/datamodel/SequenceCollectionI.js
-jalview/datamodel/SequenceCursor.js
-jalview/datamodel/SequenceFeature.js
-jalview/datamodel/SequenceGroup.js
-jalview/datamodel/SequenceI.js
-jalview/datamodel/SequenceNode.js
-jalview/datamodel/features/FeatureLocationI.js
-jalview/datamodel/features/FeatureMatcher.js
-jalview/datamodel/features/FeatureMatcherI.js
-jalview/datamodel/features/FeatureMatcherSet.js
-jalview/datamodel/features/FeatureMatcherSetI.js
-jalview/datamodel/features/FeatureStore.js
-jalview/datamodel/features/RangeComparator.js
-jalview/datamodel/features/SequenceFeatures.js
-jalview/datamodel/features/SequenceFeaturesI.js
-jalview/gui/AlignFrame.js
-jalview/gui/AlignViewport.js
-jalview/gui/AlignmentPanel.js
-jalview/gui/AnnotationLabels.js
-jalview/gui/AnnotationPanel.js
-jalview/gui/CalculationChooser.js
-jalview/gui/ColourMenuHelper.js
-jalview/gui/ComboBoxTooltipRenderer.js
-jalview/gui/Desktop.js
-jalview/gui/FeatureRenderer.js
-jalview/gui/IProgressIndicator.js
-jalview/gui/IdCanvas.js
-jalview/gui/IdPanel.js
-jalview/gui/IdwidthAdjuster.js
-jalview/gui/JalviewChangeSupport.js
-jalview/gui/JvSwingUtils.js
-jalview/gui/PaintRefresher.js
-jalview/gui/PopupMenu.js
-jalview/gui/Preferences.js
-jalview/gui/ProgressBar.js
-jalview/gui/ScalePanel.js
-jalview/gui/SeqCanvas.js
-jalview/gui/SeqPanel.js
-jalview/gui/SequenceRenderer.js
-jalview/gui/TreeCanvas.js
-jalview/gui/TreePanel.js
-jalview/gui/ViewSelectionMenu.js
-jalview/io/AlignFile.js
-jalview/io/AlignmentFileReaderI.js
-jalview/io/AlignmentFileWriterI.js
-jalview/io/AppletFormatAdapter.js
-jalview/io/DataSourceType.js
-jalview/io/FileFormat.js
-jalview/io/FileFormatI.js
-jalview/io/FileFormats.js
-jalview/io/FileLoader.js
-jalview/io/FileParse.js
-jalview/io/IdentifyFile.js
-jalview/io/PIRFile.js
-jalview/io/ScoreMatrixFile.js
-jalview/io/SequenceAnnotationReport.js
-jalview/javascript/log4j/ConsoleAppender.js
-jalview/javascript/log4j/Layout.js
-jalview/javascript/log4j/Level.js
-jalview/javascript/log4j/Logger.js
-jalview/javascript/log4j/Priority.js
-jalview/javascript/log4j/SimpleLayout.js
-jalview/javascript/log4j/spi/OptionHandler.js
-jalview/jbgui/GAlignFrame.js
-jalview/jbgui/GAlignmentPanel.js
-jalview/jbgui/GDesktop.js
-jalview/jbgui/GPreferences.js
-jalview/jbgui/GTreePanel.js
-jalview/math/Matrix.js
-jalview/math/MatrixI.js
-jalview/project/Jalview2XML.js
-jalview/renderer/AnnotationRenderer.js
-jalview/renderer/AwtRenderPanelI.js
-jalview/renderer/ResidueColourFinder.js
-jalview/renderer/ResidueShader.js
-jalview/renderer/ResidueShaderI.js
-jalview/renderer/ScaleRenderer.js
-jalview/renderer/seqfeatures/FeatureRenderer.js
-jalview/schemes/Blosum62ColourScheme.js
-jalview/schemes/BuriedColourScheme.js
-jalview/schemes/ClustalxColourScheme.js
-jalview/schemes/ColourSchemeI.js
-jalview/schemes/ColourSchemeProperty.js
-jalview/schemes/ColourSchemes.js
-jalview/schemes/Consensus.js
-jalview/schemes/FeatureColour.js
-jalview/schemes/HelixColourScheme.js
-jalview/schemes/HydrophobicColourScheme.js
-jalview/schemes/JalviewColourScheme.js
-jalview/schemes/NucleotideColourScheme.js
-jalview/schemes/PIDColourScheme.js
-jalview/schemes/PurinePyrimidineColourScheme.js
-jalview/schemes/RNAHelicesColour.js
-jalview/schemes/ResidueColourScheme.js
-jalview/schemes/ResidueProperties.js
-jalview/schemes/ScoreColourScheme.js
-jalview/schemes/StrandColourScheme.js
-jalview/schemes/TCoffeeColourScheme.js
-jalview/schemes/TaylorColourScheme.js
-jalview/schemes/TurnColourScheme.js
-jalview/schemes/ZappoColourScheme.js
-jalview/structure/CommandListener.js
-jalview/structure/SelectionListener.js
-jalview/structure/SelectionSource.js
-jalview/structure/SequenceListener.js
-jalview/structure/StructureImportSettings.js
-jalview/structure/StructureSelectionManager.js
-jalview/structure/VamsasSource.js
-jalview/urls/CustomUrlProvider.js
-jalview/urls/IdOrgSettings.js
-jalview/urls/IdentifiersUrlProvider.js
-jalview/urls/UrlLinkDisplay.js
-jalview/urls/UrlLinkTableModel.js
-jalview/urls/UrlProvider.js
-jalview/urls/UrlProviderImpl.js
-jalview/urls/api/UrlProviderFactoryI.js
-jalview/urls/api/UrlProviderI.js
-jalview/urls/desktop/DesktopUrlProviderFactory.js
-jalview/util/ColorUtils.js
-jalview/util/Comparison.js
-jalview/util/DBRefUtils.js
-jalview/util/Format.js
-jalview/util/MessageManager.js
-jalview/util/Platform.js
-jalview/util/QuickSort.js
-jalview/util/StringUtils.js
-jalview/util/UrlLink.js
-jalview/util/jarInputStreamProvider.js
-jalview/util/matcher/Condition.js
-jalview/util/matcher/Matcher.js
-jalview/util/matcher/MatcherI.js
-jalview/viewmodel/AlignmentViewport.js
-jalview/viewmodel/ViewportListenerI.js
-jalview/viewmodel/ViewportProperties.js
-jalview/viewmodel/ViewportRanges.js
-jalview/viewmodel/seqfeatures/FeatureRendererModel.js
-jalview/viewmodel/seqfeatures/FeatureRendererSettings.js
-jalview/viewmodel/seqfeatures/FeaturesDisplayed.js
-jalview/viewmodel/styles/ViewStyle.js
-jalview/workers/AlignCalcManager.js
-jalview/workers/AlignCalcWorker.js
-jalview/workers/ConsensusThread.js
-jalview/workers/ConservationThread.js
-jalview/ws/sifts/SiftsSettings.js
-jalview/xml/binding/jalview/Annotation.js
-jalview/xml/binding/jalview/AnnotationElement.js
-jalview/xml/binding/jalview/Feature.js
-jalview/xml/binding/jalview/FeatureMatcher.js
-jalview/xml/binding/jalview/FeatureMatcherSet.js
-jalview/xml/binding/jalview/FilterBy.js
-jalview/xml/binding/jalview/JalviewModel.js
-jalview/xml/binding/jalview/NoValueColour.js
-jalview/xml/binding/jalview/Pdbentry.js
-jalview/xml/binding/jalview/Sequence.js
-jalview/xml/binding/jalview/SequenceSet.js
-jalview/xml/binding/jalview/SequenceType.js
-jalview/xml/binding/jalview/VAMSAS.js
-jalview/xml/binding/jalview/WebServiceParameterSet.js
+java/applet/Applet.js
 java/applet/AppletContext.js
 java/applet/AppletStub.js
 java/applet/JSApplet.js
+java/awt/ActiveEvent.js
+java/awt/Adjustable.js
 java/awt/AWTEvent.js
 java/awt/AWTEventMulticaster.js
 java/awt/AWTKeyStroke.js
-java/awt/ActiveEvent.js
-java/awt/Adjustable.js
-java/awt/AlphaComposite.js
 java/awt/BasicStroke.js
 java/awt/BorderLayout.js
+java/awt/Button.js
 java/awt/Color.js
+java/awt/color/ColorSpace.js
 java/awt/Component.js
 java/awt/ComponentOrientation.js
-java/awt/Composite.js
-java/awt/Container.js
 java/awt/ContainerOrderFocusTraversalPolicy.js
+java/awt/Container.js
 java/awt/Cursor.js
 java/awt/DefaultFocusTraversalPolicy.js
 java/awt/DefaultKeyboardFocusManager.js
 java/awt/Dialog.js
 java/awt/Dimension.js
-java/awt/EventDispatchThread.js
-java/awt/EventFilter.js
-java/awt/EventQueue.js
-java/awt/EventQueueItem.js
-java/awt/FlowLayout.js
-java/awt/FocusTraversalPolicy.js
-java/awt/Font.js
-java/awt/FontMetrics.js
-java/awt/GraphicsCallback.js
-java/awt/GraphicsConfiguration.js
-java/awt/GraphicsDevice.js
-java/awt/GraphicsEnvironment.js
-java/awt/GridLayout.js
-java/awt/Image.js
-java/awt/Insets.js
-java/awt/ItemSelectable.js
-java/awt/JSComponent.js
-java/awt/JSDialog.js
-java/awt/JSFrame.js
-java/awt/JSPanel.js
-java/awt/KeyEventDispatcher.js
-java/awt/KeyEventPostProcessor.js
-java/awt/KeyboardFocusManager.js
-java/awt/LayoutManager.js
-java/awt/LayoutManager2.js
-java/awt/LightweightDispatcher.js
-java/awt/Paint.js
-java/awt/Point.js
-java/awt/Queue.js
-java/awt/Rectangle.js
-java/awt/RenderingHints.js
-java/awt/SentEvent.js
-java/awt/Shape.js
-java/awt/Stroke.js
-java/awt/Toolkit.js
-java/awt/Transparency.js
-java/awt/VKCollection.js
-java/awt/Window.js
-java/awt/color/ColorSpace.js
-java/awt/datatransfer/ClipboardOwner.js
-java/awt/datatransfer/FlavorMap.js
-java/awt/datatransfer/FlavorTable.js
-java/awt/datatransfer/SystemFlavorMap.js
-java/awt/dnd/DropTarget.js
-java/awt/dnd/DropTargetContext.js
-java/awt/dnd/DropTargetListener.js
 java/awt/dnd/peer/DropTargetPeer.js
-java/awt/event/AWTEventListener.js
-java/awt/event/ActionEvent.js
 java/awt/event/ActionListener.js
 java/awt/event/AdjustmentEvent.js
 java/awt/event/AdjustmentListener.js
+java/awt/event/AWTEventListener.js
 java/awt/event/ComponentAdapter.js
 java/awt/event/ComponentEvent.js
 java/awt/event/ComponentListener.js
-java/awt/event/ContainerEvent.js
 java/awt/event/ContainerListener.js
-java/awt/event/FocusAdapter.js
 java/awt/event/FocusEvent.js
 java/awt/event/FocusListener.js
 java/awt/event/HierarchyBoundsListener.js
@@ -317,13 +39,9 @@ java/awt/event/InputMethodListener.js
 java/awt/event/InvocationEvent.js
 java/awt/event/ItemEvent.js
 java/awt/event/ItemListener.js
-java/awt/event/KeyAdapter.js
-java/awt/event/KeyEvent.js
 java/awt/event/KeyListener.js
-java/awt/event/MouseAdapter.js
 java/awt/event/MouseEvent.js
 java/awt/event/MouseListener.js
-java/awt/event/MouseMotionAdapter.js
 java/awt/event/MouseMotionListener.js
 java/awt/event/MouseWheelListener.js
 java/awt/event/TextListener.js
@@ -332,124 +50,103 @@ java/awt/event/WindowEvent.js
 java/awt/event/WindowFocusListener.js
 java/awt/event/WindowListener.js
 java/awt/event/WindowStateListener.js
+java/awt/EventDispatchThread.js
+java/awt/EventFilter.js
+java/awt/EventQueue.js
+java/awt/EventQueueItem.js
+java/awt/FlowLayout.js
+java/awt/FocusTraversalPolicy.js
+java/awt/Font.js
 java/awt/font/FontRenderContext.js
+java/awt/FontMetrics.js
+java/awt/Frame.js
 java/awt/geom/AffineTransform.js
 java/awt/geom/Dimension2D.js
 java/awt/geom/Path2D.js
 java/awt/geom/PathIterator.js
 java/awt/geom/Point2D.js
-java/awt/geom/RectIterator.js
 java/awt/geom/Rectangle2D.js
 java/awt/geom/RectangularShape.js
-java/awt/image/BufferedImage.js
-java/awt/image/ColorModel.js
-java/awt/image/DataBuffer.js
-java/awt/image/DataBufferInt.js
-java/awt/image/DirectColorModel.js
+java/awt/geom/RectIterator.js
+java/awt/GraphicsCallback.js
+java/awt/GraphicsConfiguration.js
+java/awt/GraphicsDevice.js
+java/awt/GraphicsEnvironment.js
 java/awt/image/ImageObserver.js
-java/awt/image/PackedColorModel.js
-java/awt/image/Raster.js
-java/awt/image/RenderedImage.js
-java/awt/image/SampleModel.js
-java/awt/image/SinglePixelPackedSampleModel.js
-java/awt/image/WritableRaster.js
+java/awt/Insets.js
+java/awt/ItemSelectable.js
+java/awt/JSComponent.js
+java/awt/JSDialog.js
+java/awt/JSFrame.js
+java/awt/JSPanel.js
+java/awt/KeyboardFocusManager.js
+java/awt/KeyEventDispatcher.js
+java/awt/KeyEventPostProcessor.js
+java/awt/Label.js
+java/awt/LayoutManager.js
+java/awt/LayoutManager2.js
+java/awt/LightweightDispatcher.js
+java/awt/Paint.js
+java/awt/Panel.js
 java/awt/peer/ComponentPeer.js
 java/awt/peer/ContainerPeer.js
 java/awt/peer/FramePeer.js
 java/awt/peer/KeyboardFocusManagerPeer.js
 java/awt/peer/LightweightPeer.js
 java/awt/peer/WindowPeer.js
-java/awt/print/Printable.js
+java/awt/Point.js
+java/awt/Queue.js
+java/awt/Rectangle.js
+java/awt/RenderingHints.js
+java/awt/Scrollbar.js
+java/awt/ScrollPane.js
+java/awt/Shape.js
+java/awt/Stroke.js
+java/awt/TextArea.js
+java/awt/TextComponent.js
+java/awt/TextField.js
+java/awt/Toolkit.js
+java/awt/Transparency.js
+java/awt/Window.js
 java/beans/ChangeListenerMap.js
 java/beans/PropertyChangeEvent.js
 java/beans/PropertyChangeListener.js
 java/beans/PropertyChangeSupport.js
-java/io/BufferedInputStream.js
-java/io/BufferedReader.js
-java/io/ByteArrayInputStream.js
-java/io/Closeable.js
-java/io/File.js
-java/io/FileDescriptor.js
-java/io/FileInputStream.js
-java/io/FileReader.js
-java/io/FileSystem.js
-java/io/FilterInputStream.js
-java/io/InputStream.js
-java/io/InputStreamReader.js
-java/io/PushbackInputStream.js
-java/io/Reader.js
 java/lang/AbstractStringBuilder.js
-java/lang/AutoCloseable.js
 java/lang/Class.js
 java/lang/Enum.js
 java/lang/Iterable.js
-java/lang/Readable.js
-java/lang/Runtime.js
+java/lang/reflect/Constructor.js
+java/lang/reflect/Method.js
 java/lang/StringBuffer.js
 java/lang/StringBuilder.js
 java/lang/Thread.js
 java/lang/ThreadGroup.js
-java/lang/reflect/Constructor.js
-java/lang/reflect/Method.js
-java/math/BigDecimal.js
-java/math/BigInteger.js
-java/math/MathContext.js
 java/math/RoundingMode.js
-java/net/HttpURLConnection.js
-java/net/MalformedURLException.js
 java/net/URL.js
-java/net/URLConnection.js
-java/net/URLDecoder.js
-java/net/URLStreamHandler.js
 java/net/URLStreamHandlerFactory.js
-java/nio/Bits.js
-java/nio/Buffer.js
-java/nio/ByteBuffer.js
-java/nio/ByteOrder.js
-java/nio/CharBuffer.js
-java/nio/HeapByteBuffer.js
-java/nio/HeapCharBuffer.js
-java/nio/charset/Charset.js
-java/nio/charset/CharsetDecoder.js
-java/nio/charset/CoderResult.js
-java/nio/charset/CodingErrorAction.js
-java/security/AccessControlContext.js
-java/security/AccessController.js
-java/security/PrivilegedAction.js
-java/security/PrivilegedExceptionAction.js
-java/text/AttributedCharacterIterator.js
 java/text/CharacterIterator.js
-java/text/DateFormat.js
-java/text/DateFormatSymbols.js
 java/text/DecimalFormat.js
 java/text/DecimalFormatSymbols.js
 java/text/DigitList.js
 java/text/FieldPosition.js
 java/text/Format.js
-java/text/MessageFormat.js
 java/text/NumberFormat.js
-java/text/SimpleDateFormat.js
 java/util/AbstractCollection.js
 java/util/AbstractList.js
 java/util/AbstractMap.js
-java/util/AbstractQueue.js
 java/util/AbstractSequentialList.js
 java/util/AbstractSet.js
-java/util/ArrayDeque.js
 java/util/ArrayList.js
 java/util/Arrays.js
-java/util/BitSet.js
-java/util/Calendar.js
 java/util/Collection.js
 java/util/Collections.js
 java/util/Comparator.js
 java/util/Deque.js
 java/util/Dictionary.js
-java/util/DualPivotQuicksort.js
 java/util/Enumeration.js
 java/util/EventListener.js
 java/util/EventObject.js
-java/util/GregorianCalendar.js
 java/util/HashMap.js
 java/util/HashSet.js
 java/util/Hashtable.js
@@ -458,75 +155,26 @@ java/util/Iterator.js
 java/util/LinkedHashMap.js
 java/util/LinkedList.js
 java/util/List.js
-java/util/ListIterator.js
 java/util/ListResourceBundle.js
 java/util/Locale.js
 java/util/Map.js
-java/util/NavigableMap.js
-java/util/NavigableSet.js
 java/util/Objects.js
-java/util/Properties.js
-java/util/PropertyResourceBundle.js
 java/util/Queue.js
+java/util/Random.js
 java/util/RandomAccess.js
 java/util/ResourceBundle.js
 java/util/Set.js
-java/util/SortedMap.js
-java/util/SortedSet.js
-java/util/StringTokenizer.js
 java/util/TimSort.js
-java/util/TimeZone.js
-java/util/TreeMap.js
-java/util/TreeSet.js
 java/util/Vector.js
-java/util/concurrent/AbstractExecutorService.js
-java/util/concurrent/BlockingQueue.js
-java/util/concurrent/ConcurrentHashMap.js
-java/util/concurrent/ConcurrentMap.js
-java/util/concurrent/Executor.js
-java/util/concurrent/ExecutorService.js
-java/util/concurrent/Executors.js
-java/util/concurrent/LinkedBlockingQueue.js
-java/util/concurrent/RejectedExecutionHandler.js
-java/util/concurrent/Semaphore.js
-java/util/concurrent/ThreadFactory.js
-java/util/concurrent/ThreadPoolExecutor.js
-java/util/concurrent/TimeUnit.js
-java/util/concurrent/atomic/AtomicBoolean.js
-java/util/concurrent/atomic/AtomicInteger.js
-java/util/concurrent/locks/AbstractOwnableSynchronizer.js
-java/util/concurrent/locks/AbstractQueuedSynchronizer.js
-java/util/concurrent/locks/Condition.js
-java/util/concurrent/locks/Lock.js
-java/util/concurrent/locks/ReadWriteLock.js
-java/util/concurrent/locks/ReentrantLock.js
-java/util/concurrent/locks/ReentrantReadWriteLock.js
-java/util/jar/JarEntry.js
-java/util/jar/JarInputStream.js
-java/util/logging/Level.js
-java/util/logging/Logger.js
-java/util/regex/MatchResult.js
-java/util/regex/Matcher.js
-java/util/regex/Pattern.js
-java/util/zip/CRC32.js
-java/util/zip/Inflater.js
-java/util/zip/InflaterInputStream.js
-java/util/zip/ZipConstants.js
-java/util/zip/ZipEntry.js
-java/util/zip/ZipInputStream.js
-javajs/api/GenericLineReader.js
 javajs/api/JSFunction.js
-javajs/api/JSONEncodable.js
-javajs/util/AU.js
 javajs/util/AjaxURLConnection.js
-javajs/util/AjaxURLStreamHandler.js
 javajs/util/AjaxURLStreamHandlerFactory.js
-javajs/util/Encoding.js
+javajs/util/AU.js
 javajs/util/JSThread.js
 javajs/util/Lst.js
 javajs/util/PT.js
-javajs/util/Rdr.js
 javajs/util/SB.js
+javax/net/ssl/HttpsUrlConnection.js
 javax/swing/AbstractAction.js
 javax/swing/AbstractButton.js
 javax/swing/AbstractListModel.js
@@ -534,6 +182,13 @@ javax/swing/Action.js
 javax/swing/ActionMap.js
 javax/swing/AncestorNotifier.js
 javax/swing/ArrayTable.js
+javax/swing/border/AbstractBorder.js
+javax/swing/border/BevelBorder.js
+javax/swing/border/Border.js
+javax/swing/border/CompoundBorder.js
+javax/swing/border/EmptyBorder.js
+javax/swing/border/EtchedBorder.js
+javax/swing/border/LineBorder.js
 javax/swing/BorderFactory.js
 javax/swing/BoundedRangeModel.js
 javax/swing/BoxLayout.js
@@ -541,14 +196,31 @@ javax/swing/ButtonGroup.js
 javax/swing/ButtonModel.js
 javax/swing/ClientPropertyKey.js
 javax/swing/ComboBoxModel.js
-javax/swing/ComponentInputMap.js
 javax/swing/DefaultBoundedRangeModel.js
 javax/swing/DefaultButtonModel.js
 javax/swing/DefaultComboBoxModel.js
-javax/swing/DefaultDesktopManager.js
-javax/swing/DefaultListCellRenderer.js
 javax/swing/DefaultSingleSelectionModel.js
-javax/swing/DesktopManager.js
+javax/swing/DropMode.js
+javax/swing/event/AncestorEvent.js
+javax/swing/event/AncestorListener.js
+javax/swing/event/CaretEvent.js
+javax/swing/event/CaretListener.js
+javax/swing/event/ChangeEvent.js
+javax/swing/event/ChangeListener.js
+javax/swing/event/DocumentEvent.js
+javax/swing/event/DocumentListener.js
+javax/swing/event/EventListenerList.js
+javax/swing/event/ListDataEvent.js
+javax/swing/event/ListDataListener.js
+javax/swing/event/UndoableEditEvent.js
+javax/swing/event/UndoableEditListener.js
+javax/swing/FocusManager.js
+javax/swing/InternalFrameFocusTraversalPolicy.js
+javax/swing/LayoutComparator.js
+javax/swing/LayoutFocusTraversalPolicy.js
+javax/swing/SortingFocusTraversalPolicy.js
+javax/swing/SwingContainerOrderFocusTraversalPolicy.js
+javax/swing/SwingDefaultFocusTraversalPolicy.js
 javax/swing/InputMap.js
 javax/swing/JApplet.js
 javax/swing/JButton.js
@@ -556,10 +228,7 @@ javax/swing/JCheckBox.js
 javax/swing/JCheckBoxMenuItem.js
 javax/swing/JComboBox.js
 javax/swing/JComponent.js
-javax/swing/JDesktopPane.js
-javax/swing/JDialog.js
 javax/swing/JFrame.js
-javax/swing/JInternalFrame.js
 javax/swing/JLabel.js
 javax/swing/JLayeredPane.js
 javax/swing/JMenu.js
@@ -567,179 +236,124 @@ javax/swing/JMenuBar.js
 javax/swing/JMenuItem.js
 javax/swing/JPanel.js
 javax/swing/JPopupMenu.js
-javax/swing/JProgressBar.js
-javax/swing/JRadioButton.js
 javax/swing/JRadioButtonMenuItem.js
 javax/swing/JRootPane.js
 javax/swing/JScrollBar.js
 javax/swing/JScrollPane.js
 javax/swing/JSeparator.js
-javax/swing/JTabbedPane.js
+javax/swing/JTextArea.js
+javax/swing/JTextField.js
 javax/swing/JToggleButton.js
-javax/swing/JToolTip.js
 javax/swing/JViewport.js
-javax/swing/JWindow.js
-javax/swing/KeyStroke.js
 javax/swing/KeyboardManager.js
-javax/swing/ListCellRenderer.js
+javax/swing/KeyStroke.js
 javax/swing/ListModel.js
 javax/swing/LookAndFeel.js
 javax/swing/MenuElement.js
-javax/swing/MenuSelectionManager.js
 javax/swing/MutableComboBoxModel.js
-javax/swing/Popup.js
-javax/swing/PopupFactory.js
+javax/swing/plaf/ActionMapUIResource.js
+javax/swing/plaf/basic/BasicBorders.js
+javax/swing/plaf/BorderUIResource.js
+javax/swing/plaf/ColorUIResource.js
+javax/swing/plaf/ComponentUI.js
+javax/swing/plaf/DimensionUIResource.js
+javax/swing/plaf/FontUIResource.js
+javax/swing/plaf/InputMapUIResource.js
+javax/swing/plaf/InsetsUIResource.js
+javax/swing/plaf/UIResource.js
 javax/swing/RepaintManager.js
 javax/swing/RootPaneContainer.js
+javax/swing/Scrollable.js
 javax/swing/ScrollPaneConstants.js
 javax/swing/ScrollPaneLayout.js
-javax/swing/Scrollable.js
 javax/swing/SingleSelectionModel.js
 javax/swing/SizeRequirements.js
 javax/swing/SwingConstants.js
 javax/swing/SwingPaintEventDispatcher.js
 javax/swing/SwingUtilities.js
-javax/swing/Timer.js
-javax/swing/ToolTipManager.js
+javax/swing/text/AbstractDocument.js
+javax/swing/text/AttributeSet.js
+javax/swing/text/Caret.js
+javax/swing/text/DefaultCaret.js
+javax/swing/text/DefaultEditorKit.js
+javax/swing/text/Document.js
+javax/swing/text/EditorKit.js
+javax/swing/text/Element.js
+javax/swing/text/GapContent.js
+javax/swing/text/GapVector.js
+javax/swing/text/JTextComponent.js
+javax/swing/text/MutableAttributeSet.js
+javax/swing/text/PlainDocument.js
+javax/swing/text/PlainView.js
+javax/swing/text/Position.js
+javax/swing/text/Segment.js
+javax/swing/text/SegmentCache.js
+javax/swing/text/SimpleAttributeSet.js
+javax/swing/text/Style.js
+javax/swing/text/StyleConstants.js
+javax/swing/text/StyleContext.js
+javax/swing/text/TabExpander.js
+javax/swing/text/TextAction.js
+javax/swing/text/Utilities.js
+javax/swing/text/View.js
+javax/swing/tree/TreeNode.js
 javax/swing/UIDefaults.js
 javax/swing/UIManager.js
+javax/swing/undo/AbstractUndoableEdit.js
+javax/swing/undo/CompoundEdit.js
+javax/swing/undo/UndoableEdit.js
 javax/swing/ViewportLayout.js
 javax/swing/WindowConstants.js
-javax/swing/border/AbstractBorder.js
-javax/swing/border/BevelBorder.js
-javax/swing/border/Border.js
-javax/swing/border/EmptyBorder.js
-javax/swing/border/EtchedBorder.js
-javax/swing/border/LineBorder.js
-javax/swing/border/TitledBorder.js
-javax/swing/event/AncestorEvent.js
-javax/swing/event/AncestorListener.js
-javax/swing/event/ChangeEvent.js
-javax/swing/event/ChangeListener.js
-javax/swing/event/EventListenerList.js
-javax/swing/event/InternalFrameAdapter.js
-javax/swing/event/InternalFrameEvent.js
-javax/swing/event/InternalFrameListener.js
-javax/swing/event/ListDataListener.js
-javax/swing/event/MenuKeyListener.js
-javax/swing/event/MenuListener.js
-javax/swing/event/TableModelListener.js
-javax/swing/plaf/ActionMapUIResource.js
-javax/swing/plaf/BorderUIResource.js
-javax/swing/plaf/ColorUIResource.js
-javax/swing/plaf/ComponentInputMapUIResource.js
-javax/swing/plaf/ComponentUI.js
-javax/swing/plaf/DimensionUIResource.js
-javax/swing/plaf/FontUIResource.js
-javax/swing/plaf/InsetsUIResource.js
-javax/swing/plaf/UIResource.js
-javax/swing/plaf/basic/BasicBorders.js
-javax/swing/table/AbstractTableModel.js
-javax/swing/table/TableModel.js
-javax/xml/bind/ContextFinder.js
-javax/xml/bind/GetPropertyAction.js
-javax/xml/bind/JAXBContext.js
-javax/xml/bind/JAXBContextFactory.js
-javax/xml/bind/JAXBElement.js
-javax/xml/bind/ModuleUtil.js
-javax/xml/bind/ServiceLoaderUtil.js
-javax/xml/bind/Unmarshaller.js
-javax/xml/bind/ValidationEventHandler.js
-javax/xml/bind/annotation/adapters/CollapsedStringAdapter.js
-javax/xml/bind/annotation/adapters/XmlAdapter.js
-javax/xml/bind/helpers/AbstractUnmarshallerImpl.js
-javax/xml/bind/helpers/DefaultValidationEventHandler.js
-javax/xml/datatype/XMLGregorianCalendar.js
-javax/xml/namespace/QName.js
-javax/xml/stream/XMLInputFactory.js
-javax/xml/stream/XMLStreamReader.js
-org/apache/xerces/jaxp/datatype/XMLGregorianCalendarImpl.js
-org/json/simple/parser/JSONParser.js
-org/json/simple/parser/ParseException.js
-org/json/simple/parser/Yylex.js
-org/xml/sax/AttributeList.js
-org/xml/sax/Attributes.js
-org/xml/sax/ContentHandler.js
-org/xml/sax/InputSource.js
-org/xml/sax/Parser.js
-org/xml/sax/XMLReader.js
-org/xml/sax/ext/Attributes2.js
-sun/awt/AWTAccessor.js
-sun/awt/AWTAutoShutdown.js
 sun/awt/AppContext.js
+sun/awt/AWTAutoShutdown.js
 sun/awt/CausedFocusEvent.js
 sun/awt/ComponentFactory.js
-sun/awt/EventQueueItem.js
 sun/awt/KeyboardFocusManagerPeerProvider.js
 sun/awt/MostRecentKeyValue.js
 sun/awt/MostRecentThreadAppContext.js
 sun/awt/PaintEventDispatcher.js
 sun/awt/PostEventQueue.js
 sun/awt/RequestFocusController.js
-sun/awt/SunGraphicsCallback.js
 sun/awt/SunToolkit.js
 sun/awt/WindowClosingListener.js
 sun/awt/WindowClosingSupport.js
-sun/awt/image/DataStealer.js
-sun/awt/image/IntegerComponentRaster.js
-sun/awt/image/IntegerInterleavedRaster.js
-sun/awt/image/SunWritableRaster.js
 sun/font/FontDesignMetrics.js
-sun/java2d/StateTrackable.js
-sun/java2d/StateTrackableDelegate.js
-sun/nio/cs/ArrayDecoder.js
-sun/nio/cs/HistoricallyNamedCharset.js
-sun/nio/cs/StandardCharsets.js
-sun/nio/cs/ThreadLocalCoders.js
-sun/nio/cs/UTF_8.js
-sun/nio/cs/Unicode.js
 sun/swing/DefaultLookup.js
 sun/swing/SwingLazyValue.js
-sun/swing/UIAction.js
 sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
-sun/util/calendar/AbstractCalendar.js
-sun/util/calendar/BaseCalendar.js
-sun/util/calendar/CalendarDate.js
-sun/util/calendar/CalendarSystem.js
-sun/util/calendar/CalendarUtils.js
-sun/util/calendar/Gregorian.js
-sun/util/calendar/ZoneInfo.js
+sun/text/resources/en/FormatData_en.js
 sun/util/resources/LocaleData.js
+swingjs/a2s/A2SContainer.js
+swingjs/a2s/A2SEvent.js
+swingjs/a2s/A2SListener.js
+swingjs/a2s/Applet.js
+swingjs/a2s/Button.js
+swingjs/a2s/Label.js
+swingjs/a2s/Panel.js
+swingjs/a2s/Scrollbar.js
+swingjs/a2s/ScrollPane.js
+swingjs/a2s/TextArea.js
+swingjs/a2s/TextField.js
+swingjs/api/Interface.js
+swingjs/api/js/DOMNode.js
+swingjs/api/js/HTML5CanvasContext2D.js
+swingjs/api/js/JSInterface.js
+swingjs/jquery/JQueryUI.js
 swingjs/JSApp.js
-swingjs/JSApplet.js
 swingjs/JSAppletThread.js
 swingjs/JSAppletViewer.js
-swingjs/JSCharSet.js
 swingjs/JSFocusPeer.js
 swingjs/JSFontMetrics.js
 swingjs/JSFrameViewer.js
 swingjs/JSGraphics2D.js
 swingjs/JSGraphicsConfiguration.js
 swingjs/JSGraphicsEnvironment.js
-swingjs/JSKeyEvent.js
-swingjs/JSMenuManager.js
 swingjs/JSMouse.js
+swingjs/JSNullComponentPeer.js
 swingjs/JSScreenDevice.js
 swingjs/JSThreadGroup.js
 swingjs/JSToolkit.js
 swingjs/JSUtil.js
-swingjs/a2s/Dialog.js
-swingjs/api/Interface.js
-swingjs/api/js/DOMNode.js
-swingjs/api/js/HTML5CanvasContext2D.js
-swingjs/api/js/JSInterface.js
-swingjs/jquery/JQueryUI.js
-swingjs/jzlib/Adler32.js
-swingjs/jzlib/CRC32.js
-swingjs/jzlib/Checksum.js
-swingjs/jzlib/InfBlocks.js
-swingjs/jzlib/InfCodes.js
-swingjs/jzlib/InfTree.js
-swingjs/jzlib/Inflate.js
-swingjs/jzlib/Inflater.js
-swingjs/jzlib/InflaterInputStream.js
-swingjs/jzlib/ZStream.js
 swingjs/plaf/ButtonListener.js
 swingjs/plaf/DefaultMenuLayout.js
 swingjs/plaf/HTML5LookAndFeel.js
@@ -749,12 +363,9 @@ swingjs/plaf/JSCheckBoxMenuItemUI.js
 swingjs/plaf/JSCheckBoxUI.js
 swingjs/plaf/JSComboBoxUI.js
 swingjs/plaf/JSComponentUI.js
-swingjs/plaf/JSDesktopIconUI.js
-swingjs/plaf/JSDesktopPaneUI.js
 swingjs/plaf/JSEventHandler.js
 swingjs/plaf/JSFrameUI.js
 swingjs/plaf/JSGraphicsUtils.js
-swingjs/plaf/JSInternalFrameUI.js
 swingjs/plaf/JSLabelUI.js
 swingjs/plaf/JSLayeredPaneUI.js
 swingjs/plaf/JSLightweightUI.js
@@ -764,8 +375,6 @@ swingjs/plaf/JSMenuUI.js
 swingjs/plaf/JSPanelUI.js
 swingjs/plaf/JSPopupMenuSeparatorUI.js
 swingjs/plaf/JSPopupMenuUI.js
-swingjs/plaf/JSPopupUI.js
-swingjs/plaf/JSProgressBarUI.js
 swingjs/plaf/JSRadioButtonMenuItemUI.js
 swingjs/plaf/JSRadioButtonUI.js
 swingjs/plaf/JSRootPaneUI.js
@@ -773,19 +382,14 @@ swingjs/plaf/JSScrollBarUI.js
 swingjs/plaf/JSScrollPaneUI.js
 swingjs/plaf/JSSeparatorUI.js
 swingjs/plaf/JSSliderUI.js
-swingjs/plaf/JSTabbedPaneUI.js
-swingjs/plaf/JSToolTipUI.js
+swingjs/plaf/JSTextAreaUI.js
+swingjs/plaf/JSTextFieldUI.js
+swingjs/plaf/JSTextUI.js
+swingjs/plaf/JSTextViewUI.js
 swingjs/plaf/JSViewportUI.js
 swingjs/plaf/JSWindowUI.js
 swingjs/plaf/LazyActionMap.js
 swingjs/plaf/Resizer.js
-swingjs/xml/JSJAXBClass.js
-swingjs/xml/JSJAXBContext.js
-swingjs/xml/JSJAXBContextFactory.js
-swingjs/xml/JSJAXBField.js
-swingjs/xml/JSJAXBUnmarshaller.js
-swingjs/xml/JSSAXAttributes.js
-swingjs/xml/JSSAXParser.js
-swingjs/xml/JSXMLGregorianCalendarImpl.js
-swingjs/xml/JSXMLInputFactory.js
-swingjs/xml/JSXMLStreamReader.js
+swingjs/plaf/TextListener.js
+
+
diff --git a/utils/jalviewjs/classlists/jvexamplefile.txt b/utils/jalviewjs/classlists/jvexamplefile.txt
deleted file mode 100644 (file)
index ae01fe1..0000000
+++ /dev/null
@@ -1,1183 +0,0 @@
-
-com/stevesoft/pat/Any.js
-com/stevesoft/pat/Bracket.js
-com/stevesoft/pat/Branch.js
-com/stevesoft/pat/CaseMgr.js
-com/stevesoft/pat/DotMulti.js
-com/stevesoft/pat/End.js
-com/stevesoft/pat/FastBracket.js
-com/stevesoft/pat/FastChar.js
-com/stevesoft/pat/FastMulti.js
-com/stevesoft/pat/NUnicodeAlpha.js
-com/stevesoft/pat/NUnicodeCurrency.js
-com/stevesoft/pat/NUnicodeDigit.js
-com/stevesoft/pat/NUnicodeMath.js
-com/stevesoft/pat/NUnicodePunct.js
-com/stevesoft/pat/NUnicodeW.js
-com/stevesoft/pat/NUnicodeWhite.js
-com/stevesoft/pat/NoPattern.js
-com/stevesoft/pat/NullPattern.js
-com/stevesoft/pat/Or.js
-com/stevesoft/pat/OrMark.js
-com/stevesoft/pat/Pattern.js
-com/stevesoft/pat/PatternSub.js
-com/stevesoft/pat/Pthings.js
-com/stevesoft/pat/Range.js
-com/stevesoft/pat/RegHolder.js
-com/stevesoft/pat/RegOpt.js
-com/stevesoft/pat/RegRes.js
-com/stevesoft/pat/Regex.js
-com/stevesoft/pat/ReplaceRule.js
-com/stevesoft/pat/Replacer.js
-com/stevesoft/pat/Rthings.js
-com/stevesoft/pat/Skip.js
-com/stevesoft/pat/StrPos.js
-com/stevesoft/pat/StringLike.js
-com/stevesoft/pat/StringRule.js
-com/stevesoft/pat/SubMark.js
-com/stevesoft/pat/UniValidator.js
-com/stevesoft/pat/UnicodeAlpha.js
-com/stevesoft/pat/UnicodeCurrency.js
-com/stevesoft/pat/UnicodeDigit.js
-com/stevesoft/pat/UnicodeLower.js
-com/stevesoft/pat/UnicodeMath.js
-com/stevesoft/pat/UnicodePunct.js
-com/stevesoft/pat/UnicodeUpper.js
-com/stevesoft/pat/UnicodeW.js
-com/stevesoft/pat/UnicodeWhite.js
-com/stevesoft/pat/Validator.js
-com/stevesoft/pat/oneChar.js
-com/stevesoft/pat/parsePerl.js
-com/stevesoft/pat/patInf.js
-com/stevesoft/pat/patInt.js
-com/stevesoft/pat/wrap/StringWrap.js
-intervalstore/api/IntervalI.js
-intervalstore/api/IntervalStoreI.js
-intervalstore/impl/BinarySearcher.js
-intervalstore/impl/IntervalStore.js
-intervalstore/impl/NCList.js
-intervalstore/impl/NCNode.js
-jalview/analysis/AAFrequency.js
-jalview/analysis/AlignSeq.js
-jalview/analysis/AlignmentAnnotationUtils.js
-jalview/analysis/AlignmentUtils.js
-jalview/analysis/AnnotationSorter.js
-jalview/analysis/AverageDistanceTree.js
-jalview/analysis/Conservation.js
-jalview/analysis/CrossRef.js
-jalview/analysis/GeneticCodeI.js
-jalview/analysis/GeneticCodes.js
-jalview/analysis/Grouping.js
-jalview/analysis/NJTree.js
-jalview/analysis/SeqsetUtils.js
-jalview/analysis/SequenceIdMatcher.js
-jalview/analysis/TreeBuilder.js
-jalview/analysis/TreeModel.js
-jalview/analysis/scoremodels/DistanceScoreModel.js
-jalview/analysis/scoremodels/FeatureDistanceModel.js
-jalview/analysis/scoremodels/PIDModel.js
-jalview/analysis/scoremodels/ScoreMatrix.js
-jalview/analysis/scoremodels/ScoreModels.js
-jalview/analysis/scoremodels/SimilarityParams.js
-jalview/analysis/scoremodels/SimilarityScoreModel.js
-jalview/api/AlignCalcManagerI.js
-jalview/api/AlignCalcWorkerI.js
-jalview/api/AlignViewControllerGuiI.js
-jalview/api/AlignViewControllerI.js
-jalview/api/AlignViewportI.js
-jalview/api/AlignmentColsCollectionI.js
-jalview/api/AlignmentRowsCollectionI.js
-jalview/api/AlignmentViewPanel.js
-jalview/api/BuildDetailsI.js
-jalview/api/DBRefEntryI.js
-jalview/api/FeatureColourI.js
-jalview/api/FeatureRenderer.js
-jalview/api/FeatureSettingsControllerI.js
-jalview/api/FeatureSettingsModelI.js
-jalview/api/FeaturesDisplayedI.js
-jalview/api/OOMHandlerI.js
-jalview/api/RendererListenerI.js
-jalview/api/SequenceRenderer.js
-jalview/api/SequenceStructureBinding.js
-jalview/api/StructureSelectionManagerProvider.js
-jalview/api/ViewStyleI.js
-jalview/api/analysis/PairwiseScoreModelI.js
-jalview/api/analysis/ScoreModelI.js
-jalview/api/analysis/SimilarityParamsI.js
-jalview/api/structures/JalviewStructureDisplayI.js
-jalview/bin/ArgsParser.js
-jalview/bin/BuildDetails.js
-jalview/bin/Cache.js
-jalview/bin/Jalview.js
-jalview/controller/AlignViewController.js
-jalview/datamodel/ASequence.js
-jalview/datamodel/ASequenceI.js
-jalview/datamodel/Alignment.js
-jalview/datamodel/AlignmentAnnotation.js
-jalview/datamodel/AlignmentI.js
-jalview/datamodel/AlignmentView.js
-jalview/datamodel/AnnotatedCollectionI.js
-jalview/datamodel/Annotation.js
-jalview/datamodel/BinaryNode.js
-jalview/datamodel/CigarArray.js
-jalview/datamodel/CigarBase.js
-jalview/datamodel/CigarSimple.js
-jalview/datamodel/ColumnSelection.js
-jalview/datamodel/ContiguousI.js
-jalview/datamodel/DBRefEntry.js
-jalview/datamodel/DBRefSource.js
-jalview/datamodel/GraphLine.js
-jalview/datamodel/HiddenColumns.js
-jalview/datamodel/HiddenColumnsCursor.js
-jalview/datamodel/HiddenCursorPosition.js
-jalview/datamodel/HiddenSequences.js
-jalview/datamodel/Mapping.js
-jalview/datamodel/PDBEntry.js
-jalview/datamodel/Profile.js
-jalview/datamodel/ProfileI.js
-jalview/datamodel/Profiles.js
-jalview/datamodel/ProfilesI.js
-jalview/datamodel/Range.js
-jalview/datamodel/RangeElementsIterator.js
-jalview/datamodel/RangeIterator.js
-jalview/datamodel/ResidueCount.js
-jalview/datamodel/SearchResultMatchI.js
-jalview/datamodel/SearchResults.js
-jalview/datamodel/SearchResultsI.js
-jalview/datamodel/SeqCigar.js
-jalview/datamodel/Sequence.js
-jalview/datamodel/SequenceCollectionI.js
-jalview/datamodel/SequenceCursor.js
-jalview/datamodel/SequenceFeature.js
-jalview/datamodel/SequenceGroup.js
-jalview/datamodel/SequenceI.js
-jalview/datamodel/SequenceNode.js
-jalview/datamodel/StartRegionIterator.js
-jalview/datamodel/StructureViewerModel.js
-jalview/datamodel/VisibleColsCollection.js
-jalview/datamodel/VisibleContigsIterator.js
-jalview/datamodel/VisibleRowsCollection.js
-jalview/datamodel/VisibleRowsIterator.js
-jalview/datamodel/features/FeatureAttributes.js
-jalview/datamodel/features/FeatureLocationI.js
-jalview/datamodel/features/FeatureMatcherSet.js
-jalview/datamodel/features/FeatureMatcherSetI.js
-jalview/datamodel/features/FeatureSources.js
-jalview/datamodel/features/FeatureStore.js
-jalview/datamodel/features/SequenceFeatures.js
-jalview/datamodel/features/SequenceFeaturesI.js
-jalview/ext/ensembl/EnsemblData.js
-jalview/ext/ensembl/EnsemblGene.js
-jalview/ext/ensembl/EnsemblGenomes.js
-jalview/ext/ensembl/EnsemblRestClient.js
-jalview/ext/ensembl/EnsemblSeqProxy.js
-jalview/ext/ensembl/EnsemblSequenceFetcher.js
-jalview/ext/jmol/JalviewJmolBinding.js
-jalview/ext/jmol/JmolCommands.js
-jalview/ext/jmol/JmolParser.js
-jalview/fts/api/FTSData.js
-jalview/fts/api/FTSDataColumnI.js
-jalview/fts/api/FTSRestClientI.js
-jalview/fts/api/GFTSPanelI.js
-jalview/fts/core/DecimalFormatTableCellRenderer.js
-jalview/fts/core/FTSDataColumnPreferences.js
-jalview/fts/core/FTSRestClient.js
-jalview/fts/core/FTSRestRequest.js
-jalview/fts/core/FTSRestResponse.js
-jalview/fts/core/GFTSPanel.js
-jalview/fts/service/pdb/PDBFTSPanel.js
-jalview/fts/service/pdb/PDBFTSRestClient.js
-jalview/fts/service/uniprot/UniProtFTSRestClient.js
-jalview/fts/service/uniprot/UniprotFTSPanel.js
-jalview/gui/AlignFrame.js
-jalview/gui/AlignViewport.js
-jalview/gui/AlignmentPanel.js
-jalview/gui/AnnotationColumnChooser.js
-jalview/gui/AnnotationLabels.js
-jalview/gui/AnnotationPanel.js
-jalview/gui/AnnotationRowFilter.js
-jalview/gui/AppJmol.js
-jalview/gui/AppJmolBinding.js
-jalview/gui/CalculationChooser.js
-jalview/gui/ColourMenuHelper.js
-jalview/gui/ComboBoxTooltipRenderer.js
-jalview/gui/Desktop.js
-jalview/gui/FeatureRenderer.js
-jalview/gui/FeatureSettings.js
-jalview/gui/FeatureTypeSettings.js
-jalview/gui/FontChooser.js
-jalview/gui/IProgressIndicator.js
-jalview/gui/IdCanvas.js
-jalview/gui/IdPanel.js
-jalview/gui/IdwidthAdjuster.js
-jalview/gui/JalviewBooleanRadioButtons.js
-jalview/gui/JalviewChangeSupport.js
-jalview/gui/JalviewColourChooser.js
-jalview/gui/JalviewDialog.js
-jalview/gui/JvSwingUtils.js
-jalview/gui/OverviewCanvas.js
-jalview/gui/OverviewPanel.js
-jalview/gui/PaintRefresher.js
-jalview/gui/PopupMenu.js
-jalview/gui/Preferences.js
-jalview/gui/ProgressBar.js
-jalview/gui/ProgressPanel.js
-jalview/gui/ScalePanel.js
-jalview/gui/SeqCanvas.js
-jalview/gui/SeqPanel.js
-jalview/gui/SequenceFetcher.js
-jalview/gui/SequenceRenderer.js
-jalview/gui/SliderPanel.js
-jalview/gui/StructureChooser.js
-jalview/gui/StructureViewer.js
-jalview/gui/StructureViewerBase.js
-jalview/gui/TreeCanvas.js
-jalview/gui/TreePanel.js
-jalview/gui/ViewSelectionMenu.js
-jalview/io/AlignFile.js
-jalview/io/AlignmentFileReaderI.js
-jalview/io/AlignmentFileWriterI.js
-jalview/io/AnnotationFile.js
-jalview/io/AppletFormatAdapter.js
-jalview/io/DataSourceType.js
-jalview/io/FastaFile.js
-jalview/io/FileFormat.js
-jalview/io/FileFormatI.js
-jalview/io/FileFormats.js
-jalview/io/FileLoader.js
-jalview/io/FileParse.js
-jalview/io/FormatAdapter.js
-jalview/io/IdentifyFile.js
-jalview/io/JPredFile.js
-jalview/io/JalviewFileChooser.js
-jalview/io/JalviewFileFilter.js
-jalview/io/JalviewFileView.js
-jalview/io/JnetAnnotationMaker.js
-jalview/io/NewickFile.js
-jalview/io/PDBFeatureSettings.js
-jalview/io/PIRFile.js
-jalview/io/ScoreMatrixFile.js
-jalview/io/SequenceAnnotationReport.js
-jalview/io/StructureFile.js
-jalview/io/TCoffeeScoreFile.js
-jalview/io/cache/JvCacheableInputBox.js
-jalview/javascript/json/JSON.js
-jalview/javascript/log4j/ConsoleAppender.js
-jalview/javascript/log4j/Layout.js
-jalview/javascript/log4j/Level.js
-jalview/javascript/log4j/Logger.js
-jalview/javascript/log4j/Priority.js
-jalview/javascript/log4j/SimpleLayout.js
-jalview/javascript/log4j/spi/OptionHandler.js
-jalview/javascript/web/Client.js
-jalview/javascript/web/ClientResponse.js
-jalview/javascript/web/WebResource.js
-jalview/jbgui/BackupFilesPresetEntry.js
-jalview/jbgui/GAlignFrame.js
-jalview/jbgui/GAlignmentPanel.js
-jalview/jbgui/GDesktop.js
-jalview/jbgui/GFontChooser.js
-jalview/jbgui/GPreferences.js
-jalview/jbgui/GSliderPanel.js
-jalview/jbgui/GStructureChooser.js
-jalview/jbgui/GStructureViewer.js
-jalview/jbgui/GTreePanel.js
-jalview/jbgui/IntKeyStringValueEntry.js
-jalview/math/Matrix.js
-jalview/math/MatrixI.js
-jalview/project/Jalview2XML.js
-jalview/renderer/AnnotationRenderer.js
-jalview/renderer/AwtRenderPanelI.js
-jalview/renderer/OverviewRenderer.js
-jalview/renderer/OverviewResColourFinder.js
-jalview/renderer/ResidueColourFinder.js
-jalview/renderer/ResidueShader.js
-jalview/renderer/ResidueShaderI.js
-jalview/renderer/ScaleRenderer.js
-jalview/renderer/seqfeatures/FeatureColourFinder.js
-jalview/renderer/seqfeatures/FeatureRenderer.js
-jalview/schemes/Blosum62ColourScheme.js
-jalview/schemes/BuriedColourScheme.js
-jalview/schemes/ClustalxColourScheme.js
-jalview/schemes/ColourSchemeI.js
-jalview/schemes/ColourSchemeProperty.js
-jalview/schemes/ColourSchemes.js
-jalview/schemes/Consensus.js
-jalview/schemes/FeatureColour.js
-jalview/schemes/FeatureSettingsAdapter.js
-jalview/schemes/HelixColourScheme.js
-jalview/schemes/HydrophobicColourScheme.js
-jalview/schemes/IdColourScheme.js
-jalview/schemes/JalviewColourScheme.js
-jalview/schemes/NucleotideColourScheme.js
-jalview/schemes/PIDColourScheme.js
-jalview/schemes/PurinePyrimidineColourScheme.js
-jalview/schemes/RNAHelicesColour.js
-jalview/schemes/ResidueColourScheme.js
-jalview/schemes/ResidueProperties.js
-jalview/schemes/ScoreColourScheme.js
-jalview/schemes/StrandColourScheme.js
-jalview/schemes/TCoffeeColourScheme.js
-jalview/schemes/TaylorColourScheme.js
-jalview/schemes/TurnColourScheme.js
-jalview/schemes/ZappoColourScheme.js
-jalview/structure/AtomSpec.js
-jalview/structure/CommandListener.js
-jalview/structure/SelectionListener.js
-jalview/structure/SelectionSource.js
-jalview/structure/SequenceListener.js
-jalview/structure/StructureImportSettings.js
-jalview/structure/StructureListener.js
-jalview/structure/StructureMapping.js
-jalview/structure/StructureMappingcommandSet.js
-jalview/structure/StructureSelectionManager.js
-jalview/structure/VamsasSource.js
-jalview/structures/models/AAStructureBindingModel.js
-jalview/structures/models/SequenceStructureBindingModel.js
-jalview/urls/CustomUrlProvider.js
-jalview/urls/IdOrgSettings.js
-jalview/urls/IdentifiersUrlProvider.js
-jalview/urls/UrlLinkDisplay.js
-jalview/urls/UrlLinkTableModel.js
-jalview/urls/UrlProvider.js
-jalview/urls/UrlProviderImpl.js
-jalview/urls/api/UrlProviderFactoryI.js
-jalview/urls/api/UrlProviderI.js
-jalview/urls/desktop/DesktopUrlProviderFactory.js
-jalview/util/CaseInsensitiveString.js
-jalview/util/ColorUtils.js
-jalview/util/Comparison.js
-jalview/util/DBRefUtils.js
-jalview/util/Format.js
-jalview/util/LinkedIdentityHashSet.js
-jalview/util/MapList.js
-jalview/util/MessageManager.js
-jalview/util/Platform.js
-jalview/util/QuickSort.js
-jalview/util/SetUtils.js
-jalview/util/StringUtils.js
-jalview/util/UrlLink.js
-jalview/util/dialogrunner/DialogRunnerI.js
-jalview/util/jarInputStreamProvider.js
-jalview/viewmodel/AlignmentViewport.js
-jalview/viewmodel/OverviewDimensions.js
-jalview/viewmodel/OverviewDimensionsHideHidden.js
-jalview/viewmodel/ViewportListenerI.js
-jalview/viewmodel/ViewportProperties.js
-jalview/viewmodel/ViewportRanges.js
-jalview/viewmodel/annotationfilter/AnnotationFilterParameter.js
-jalview/viewmodel/seqfeatures/FeatureRendererModel.js
-jalview/viewmodel/seqfeatures/FeatureRendererSettings.js
-jalview/viewmodel/seqfeatures/FeaturesDisplayed.js
-jalview/viewmodel/styles/ViewStyle.js
-jalview/workers/AlignCalcManager.js
-jalview/workers/AlignCalcWorker.js
-jalview/workers/ComplementConsensusThread.js
-jalview/workers/ConsensusThread.js
-jalview/workers/ConservationThread.js
-jalview/workers/StrucConsensusThread.js
-jalview/ws/SequenceFetcher.js
-jalview/ws/dbsources/EbiFileRetrievedProxy.js
-jalview/ws/dbsources/EmblCdsSource.js
-jalview/ws/dbsources/EmblSource.js
-jalview/ws/dbsources/EmblXmlSource.js
-jalview/ws/dbsources/Pdb.js
-jalview/ws/dbsources/Pfam.js
-jalview/ws/dbsources/PfamFull.js
-jalview/ws/dbsources/PfamSeed.js
-jalview/ws/dbsources/Rfam.js
-jalview/ws/dbsources/RfamSeed.js
-jalview/ws/dbsources/Uniprot.js
-jalview/ws/dbsources/Xfam.js
-jalview/ws/ebi/EBIFetchClient.js
-jalview/ws/seqfetcher/ASequenceFetcher.js
-jalview/ws/seqfetcher/DbSourceProxy.js
-jalview/ws/seqfetcher/DbSourceProxyImpl.js
-jalview/ws/sifts/SiftsSettings.js
-jalview/xml/binding/jalview/Annotation.js
-jalview/xml/binding/jalview/AnnotationElement.js
-jalview/xml/binding/jalview/Feature.js
-jalview/xml/binding/jalview/JalviewModel.js
-jalview/xml/binding/jalview/ObjectFactory.js
-jalview/xml/binding/jalview/Pdbentry.js
-jalview/xml/binding/jalview/Sequence.js
-jalview/xml/binding/jalview/SequenceSet.js
-jalview/xml/binding/jalview/SequenceType.js
-jalview/xml/binding/jalview/VAMSAS.js
-jalview/xml/binding/jalview/WebServiceParameterSet.js
-jalview/xml/binding/uniprot/CitationType.js
-jalview/xml/binding/uniprot/CommentType.js
-jalview/xml/binding/uniprot/ConsortiumType.js
-jalview/xml/binding/uniprot/DbReferenceType.js
-jalview/xml/binding/uniprot/Entry.js
-jalview/xml/binding/uniprot/EventType.js
-jalview/xml/binding/uniprot/EvidenceType.js
-jalview/xml/binding/uniprot/EvidencedStringType.js
-jalview/xml/binding/uniprot/FeatureType.js
-jalview/xml/binding/uniprot/GeneNameType.js
-jalview/xml/binding/uniprot/GeneType.js
-jalview/xml/binding/uniprot/IsoformType.js
-jalview/xml/binding/uniprot/KeywordType.js
-jalview/xml/binding/uniprot/LocationType.js
-jalview/xml/binding/uniprot/NameListType.js
-jalview/xml/binding/uniprot/OrganismNameType.js
-jalview/xml/binding/uniprot/OrganismType.js
-jalview/xml/binding/uniprot/PersonType.js
-jalview/xml/binding/uniprot/PositionType.js
-jalview/xml/binding/uniprot/PropertyType.js
-jalview/xml/binding/uniprot/ProteinExistenceType.js
-jalview/xml/binding/uniprot/ProteinType.js
-jalview/xml/binding/uniprot/ReferenceType.js
-jalview/xml/binding/uniprot/SequenceType.js
-jalview/xml/binding/uniprot/SourceDataType.js
-jalview/xml/binding/uniprot/SourceType.js
-jalview/xml/binding/uniprot/SubcellularLocationType.js
-jalview/xml/binding/uniprot/Uniprot.js
-java/awt/AWTKeyStroke.js
-java/awt/AlphaComposite.js
-java/awt/CardLayout.js
-java/awt/Composite.js
-java/awt/ContainerOrderFocusTraversalPolicy.js
-java/awt/DefaultFocusTraversalPolicy.js
-java/awt/DefaultKeyboardFocusManager.js
-java/awt/FocusTraversalPolicy.js
-java/awt/GridBagConstraints.js
-java/awt/GridBagLayout.js
-java/awt/GridBagLayoutInfo.js
-java/awt/GridLayout.js
-java/awt/Image.js
-java/awt/KeyEventDispatcher.js
-java/awt/KeyEventPostProcessor.js
-java/awt/KeyboardFocusManager.js
-java/awt/SentEvent.js
-java/awt/TextComponent.js
-java/awt/VKCollection.js
-java/awt/datatransfer/ClipboardOwner.js
-java/awt/datatransfer/DataFlavor.js
-java/awt/datatransfer/FlavorMap.js
-java/awt/datatransfer/FlavorTable.js
-java/awt/datatransfer/MimeType.js
-java/awt/datatransfer/MimeTypeParameterList.js
-java/awt/datatransfer/SystemFlavorMap.js
-java/awt/datatransfer/Transferable.js
-java/awt/dnd/DropTarget.js
-java/awt/dnd/DropTargetContext.js
-java/awt/dnd/DropTargetDropEvent.js
-java/awt/dnd/DropTargetEvent.js
-java/awt/dnd/DropTargetListener.js
-java/awt/dnd/peer/DropTargetContextPeer.js
-java/awt/dnd/peer/DropTargetPeer.js
-java/awt/event/ActionEvent.js
-java/awt/event/ComponentAdapter.js
-java/awt/event/ContainerEvent.js
-java/awt/event/FocusAdapter.js
-java/awt/event/KeyAdapter.js
-java/awt/event/KeyEvent.js
-java/awt/event/MouseAdapter.js
-java/awt/event/MouseMotionAdapter.js
-java/awt/event/MouseWheelEvent.js
-java/awt/font/TextAttribute.js
-java/awt/geom/Path2D.js
-java/awt/geom/PathIterator.js
-java/awt/geom/RectIterator.js
-java/awt/image/BufferedImage.js
-java/awt/image/ColorModel.js
-java/awt/image/DataBuffer.js
-java/awt/image/DataBufferInt.js
-java/awt/image/DirectColorModel.js
-java/awt/image/ImageConsumer.js
-java/awt/image/ImageProducer.js
-java/awt/image/PackedColorModel.js
-java/awt/image/PixelGrabber.js
-java/awt/image/Raster.js
-java/awt/image/RenderedImage.js
-java/awt/image/SampleModel.js
-java/awt/image/SinglePixelPackedSampleModel.js
-java/awt/image/WritableRaster.js
-java/awt/peer/DialogPeer.js
-java/awt/peer/KeyboardFocusManagerPeer.js
-java/awt/print/Printable.js
-java/io/BufferedInputStream.js
-java/io/BufferedReader.js
-java/io/BufferedWriter.js
-java/io/ByteArrayInputStream.js
-java/io/ByteArrayOutputStream.js
-java/io/Closeable.js
-java/io/File.js
-java/io/FileDescriptor.js
-java/io/FileInputStream.js
-java/io/FileOutputStream.js
-java/io/FileReader.js
-java/io/FileSystem.js
-java/io/FilenameFilter.js
-java/io/FilterInputStream.js
-java/io/FilterOutputStream.js
-java/io/InputStream.js
-java/io/InputStreamReader.js
-java/io/ObjectStreamField.js
-java/io/OutputStream.js
-java/io/OutputStreamWriter.js
-java/io/PrintStream.js
-java/io/PrintWriter.js
-java/io/PushbackInputStream.js
-java/io/Reader.js
-java/io/StringReader.js
-java/io/StringWriter.js
-java/io/Writer.js
-java/lang/AutoCloseable.js
-java/lang/Iterable.js
-java/lang/Readable.js
-java/lang/Runtime.js
-java/lang/StringBuilder.js
-java/lang/reflect/Constructor.js
-java/lang/reflect/Method.js
-java/math/BigDecimal.js
-java/math/BigInteger.js
-java/math/MathContext.js
-java/math/RoundingMode.js
-java/net/HttpURLConnection.js
-java/net/HttpsURLConnection.js
-java/net/URI.js
-java/net/URLConnection.js
-java/net/URLDecoder.js
-java/net/URLStreamHandler.js
-java/nio/Bits.js
-java/nio/Buffer.js
-java/nio/ByteBuffer.js
-java/nio/ByteOrder.js
-java/nio/CharBuffer.js
-java/nio/HeapByteBuffer.js
-java/nio/HeapCharBuffer.js
-java/nio/charset/Charset.js
-java/nio/charset/CharsetDecoder.js
-java/nio/charset/CoderResult.js
-java/nio/charset/CodingErrorAction.js
-java/security/AccessControlContext.js
-java/security/AccessController.js
-java/security/PrivilegedAction.js
-java/security/PrivilegedExceptionAction.js
-java/text/AttributedCharacterIterator.js
-java/text/CharacterIterator.js
-java/text/DateFormat.js
-java/text/DateFormatSymbols.js
-java/text/DecimalFormat.js
-java/text/DecimalFormatSymbols.js
-java/text/DigitList.js
-java/text/FieldPosition.js
-java/text/Format.js
-java/text/MessageFormat.js
-java/text/NumberFormat.js
-java/text/SimpleDateFormat.js
-java/util/AbstractQueue.js
-java/util/AbstractSequentialList.js
-java/util/ArrayDeque.js
-java/util/BitSet.js
-java/util/Calendar.js
-java/util/Collection.js
-java/util/ComparableTimSort.js
-java/util/Comparator.js
-java/util/Deque.js
-java/util/DualPivotQuicksort.js
-java/util/Enumeration.js
-java/util/Formatter.js
-java/util/GregorianCalendar.js
-java/util/Iterator.js
-java/util/LinkedHashMap.js
-java/util/LinkedHashSet.js
-java/util/LinkedList.js
-java/util/List.js
-java/util/ListIterator.js
-java/util/ListResourceBundle.js
-java/util/Map.js
-java/util/NavigableMap.js
-java/util/NavigableSet.js
-java/util/Objects.js
-java/util/Properties.js
-java/util/PropertyResourceBundle.js
-java/util/Queue.js
-java/util/RandomAccess.js
-java/util/ResourceBundle.js
-java/util/Set.js
-java/util/SortedMap.js
-java/util/SortedSet.js
-java/util/StringTokenizer.js
-java/util/TimSort.js
-java/util/TimeZone.js
-java/util/TreeMap.js
-java/util/TreeSet.js
-java/util/WeakHashMap.js
-java/util/concurrent/AbstractExecutorService.js
-java/util/concurrent/BlockingQueue.js
-java/util/concurrent/ConcurrentHashMap.js
-java/util/concurrent/ConcurrentMap.js
-java/util/concurrent/Executor.js
-java/util/concurrent/ExecutorService.js
-java/util/concurrent/Executors.js
-java/util/concurrent/LinkedBlockingQueue.js
-java/util/concurrent/RejectedExecutionHandler.js
-java/util/concurrent/Semaphore.js
-java/util/concurrent/ThreadFactory.js
-java/util/concurrent/ThreadPoolExecutor.js
-java/util/concurrent/TimeUnit.js
-java/util/concurrent/atomic/AtomicBoolean.js
-java/util/concurrent/atomic/AtomicInteger.js
-java/util/concurrent/locks/AbstractOwnableSynchronizer.js
-java/util/concurrent/locks/AbstractQueuedSynchronizer.js
-java/util/concurrent/locks/Condition.js
-java/util/concurrent/locks/Lock.js
-java/util/concurrent/locks/ReadWriteLock.js
-java/util/concurrent/locks/ReentrantLock.js
-java/util/concurrent/locks/ReentrantReadWriteLock.js
-java/util/function/Function.js
-java/util/jar/JarEntry.js
-java/util/jar/JarInputStream.js
-java/util/logging/Level.js
-java/util/logging/Logger.js
-java/util/regex/MatchResult.js
-java/util/regex/Matcher.js
-java/util/regex/Pattern.js
-java/util/zip/CRC32.js
-java/util/zip/Inflater.js
-java/util/zip/InflaterInputStream.js
-java/util/zip/ZipConstants.js
-java/util/zip/ZipEntry.js
-java/util/zip/ZipInputStream.js
-javajs/api/BytePoster.js
-javajs/api/GenericCifDataParser.js
-javajs/api/GenericLineReader.js
-javajs/api/GenericOutputChannel.js
-javajs/api/JSONEncodable.js
-javajs/awt/Font.js
-javajs/awt/FontManager.js
-javajs/util/A4.js
-javajs/util/AjaxURLStreamHandler.js
-javajs/util/BS.js
-javajs/util/Base64.js
-javajs/util/CU.js
-javajs/util/CifDataParser.js
-javajs/util/DF.js
-javajs/util/Encoding.js
-javajs/util/LimitedLineReader.js
-javajs/util/M3.js
-javajs/util/M34.js
-javajs/util/M4.js
-javajs/util/OC.js
-javajs/util/P3.js
-javajs/util/P3i.js
-javajs/util/P4.js
-javajs/util/Rdr.js
-javajs/util/SB.js
-javajs/util/T3.js
-javajs/util/T3i.js
-javajs/util/T4.js
-javajs/util/V3.js
-javax/swing/AbstractAction.js
-javax/swing/AbstractCellEditor.js
-javax/swing/AbstractSpinnerModel.js
-javax/swing/Action.js
-javax/swing/Autoscroller.js
-javax/swing/CellEditor.js
-javax/swing/CellRendererPane.js
-javax/swing/ColorChooserDialog.js
-javax/swing/ComponentInputMap.js
-javax/swing/DefaultCellEditor.js
-javax/swing/DefaultDesktopManager.js
-javax/swing/DefaultListCellRenderer.js
-javax/swing/DefaultListSelectionModel.js
-javax/swing/DefaultRowSorter.js
-javax/swing/DesktopManager.js
-javax/swing/DropMode.js
-javax/swing/Icon.js
-javax/swing/ImageIcon.js
-javax/swing/InputMap.js
-javax/swing/JColorChooser.js
-javax/swing/JDesktopPane.js
-javax/swing/JDialog.js
-javax/swing/JFileChooser.js
-javax/swing/JFormattedTextField.js
-javax/swing/JInternalFrame.js
-javax/swing/JList.js
-javax/swing/JOptionPane.js
-javax/swing/JProgressBar.js
-javax/swing/JRadioButton.js
-javax/swing/JScrollPane.js
-javax/swing/JSlider.js
-javax/swing/JSpinner.js
-javax/swing/JTabbedPane.js
-javax/swing/JTable.js
-javax/swing/JTextArea.js
-javax/swing/JTextField.js
-javax/swing/JToolTip.js
-javax/swing/JViewport.js
-javax/swing/JWindow.js
-javax/swing/KeyStroke.js
-javax/swing/ListCellRenderer.js
-javax/swing/ListSelectionModel.js
-javax/swing/MenuSelectionManager.js
-javax/swing/Popup.js
-javax/swing/PopupFactory.js
-javax/swing/RowFilter.js
-javax/swing/RowSorter.js
-javax/swing/ScrollPaneConstants.js
-javax/swing/ScrollPaneLayout.js
-javax/swing/Scrollable.js
-javax/swing/SortOrder.js
-javax/swing/SpinnerModel.js
-javax/swing/SpinnerNumberModel.js
-javax/swing/Spring.js
-javax/swing/SpringLayout.js
-javax/swing/Timer.js
-javax/swing/ToolTipManager.js
-javax/swing/ViewportLayout.js
-javax/swing/border/CompoundBorder.js
-javax/swing/border/LineBorder.js
-javax/swing/border/MatteBorder.js
-javax/swing/border/TitledBorder.js
-javax/swing/colorchooser/AbstractColorChooserPanel.js
-javax/swing/colorchooser/CenterLayout.js
-javax/swing/colorchooser/ColorChooserComponentFactory.js
-javax/swing/colorchooser/ColorSelectionModel.js
-javax/swing/colorchooser/DefaultColorSelectionModel.js
-javax/swing/colorchooser/DefaultPreviewPanel.js
-javax/swing/colorchooser/DefaultRGBChooserPanel.js
-javax/swing/colorchooser/DefaultSwatchChooserPanel.js
-javax/swing/colorchooser/MainSwatchPanel.js
-javax/swing/colorchooser/RecentSwatchPanel.js
-javax/swing/colorchooser/SmartGridLayout.js
-javax/swing/colorchooser/SwatchPanel.js
-javax/swing/event/CaretEvent.js
-javax/swing/event/CaretListener.js
-javax/swing/event/CellEditorListener.js
-javax/swing/event/DocumentEvent.js
-javax/swing/event/DocumentListener.js
-javax/swing/event/InternalFrameAdapter.js
-javax/swing/event/InternalFrameEvent.js
-javax/swing/event/InternalFrameListener.js
-javax/swing/event/ListSelectionEvent.js
-javax/swing/event/ListSelectionListener.js
-javax/swing/event/MenuKeyListener.js
-javax/swing/event/MenuListener.js
-javax/swing/event/MouseInputListener.js
-javax/swing/event/RowSorterEvent.js
-javax/swing/event/RowSorterListener.js
-javax/swing/event/SwingPropertyChangeSupport.js
-javax/swing/event/TableColumnModelEvent.js
-javax/swing/event/TableColumnModelListener.js
-javax/swing/event/TableModelEvent.js
-javax/swing/event/TableModelListener.js
-javax/swing/event/UndoableEditEvent.js
-javax/swing/event/UndoableEditListener.js
-javax/swing/filechooser/FileFilter.js
-javax/swing/filechooser/FileView.js
-javax/swing/plaf/ComponentInputMapUIResource.js
-javax/swing/plaf/InputMapUIResource.js
-javax/swing/plaf/basic/BasicBorders.js
-javax/swing/table/AbstractTableModel.js
-javax/swing/table/DefaultTableCellRenderer.js
-javax/swing/table/DefaultTableColumnModel.js
-javax/swing/table/DefaultTableModel.js
-javax/swing/table/JTableHeader.js
-javax/swing/table/TableCellEditor.js
-javax/swing/table/TableCellRenderer.js
-javax/swing/table/TableColumn.js
-javax/swing/table/TableColumnModel.js
-javax/swing/table/TableModel.js
-javax/swing/table/TableRowSorter.js
-javax/swing/text/AbstractDocument.js
-javax/swing/text/AttributeSet.js
-javax/swing/text/Caret.js
-javax/swing/text/DefaultCaret.js
-javax/swing/text/DefaultEditorKit.js
-javax/swing/text/DefaultFormatter.js
-javax/swing/text/DefaultFormatterFactory.js
-javax/swing/text/Document.js
-javax/swing/text/DocumentFilter.js
-javax/swing/text/EditorKit.js
-javax/swing/text/Element.js
-javax/swing/text/GapContent.js
-javax/swing/text/GapVector.js
-javax/swing/text/InternationalFormatter.js
-javax/swing/text/JTextComponent.js
-javax/swing/text/MutableAttributeSet.js
-javax/swing/text/NumberFormatter.js
-javax/swing/text/PlainDocument.js
-javax/swing/text/PlainView.js
-javax/swing/text/Position.js
-javax/swing/text/Segment.js
-javax/swing/text/SegmentCache.js
-javax/swing/text/SimpleAttributeSet.js
-javax/swing/text/Style.js
-javax/swing/text/StyleConstants.js
-javax/swing/text/StyleContext.js
-javax/swing/text/TabExpander.js
-javax/swing/text/TextAction.js
-javax/swing/text/Utilities.js
-javax/swing/text/View.js
-javax/swing/tree/TreeCellEditor.js
-javax/swing/tree/TreeNode.js
-javax/swing/undo/AbstractUndoableEdit.js
-javax/swing/undo/CompoundEdit.js
-javax/swing/undo/UndoableEdit.js
-javax/xml/bind/ContextFinder.js
-javax/xml/bind/GetPropertyAction.js
-javax/xml/bind/JAXBContext.js
-javax/xml/bind/JAXBContextFactory.js
-javax/xml/bind/JAXBElement.js
-javax/xml/bind/ModuleUtil.js
-javax/xml/bind/ServiceLoaderUtil.js
-javax/xml/bind/Unmarshaller.js
-javax/xml/bind/ValidationEventHandler.js
-javax/xml/bind/annotation/adapters/CollapsedStringAdapter.js
-javax/xml/bind/annotation/adapters/XmlAdapter.js
-javax/xml/bind/helpers/AbstractUnmarshallerImpl.js
-javax/xml/bind/helpers/DefaultValidationEventHandler.js
-javax/xml/datatype/DatatypeFactory.js
-javax/xml/datatype/XMLGregorianCalendar.js
-javax/xml/namespace/QName.js
-javax/xml/stream/XMLInputFactory.js
-javax/xml/stream/XMLStreamReader.js
-mc_view/Atom.js
-mc_view/Bond.js
-mc_view/PDBChain.js
-mc_view/Residue.js
-net/miginfocom/layout/AC.js
-net/miginfocom/layout/AnimSpec.js
-net/miginfocom/layout/BoundSize.js
-net/miginfocom/layout/CC.js
-net/miginfocom/layout/ComponentWrapper.js
-net/miginfocom/layout/ConstraintParser.js
-net/miginfocom/layout/ContainerWrapper.js
-net/miginfocom/layout/DimConstraint.js
-net/miginfocom/layout/Grid.js
-net/miginfocom/layout/LC.js
-net/miginfocom/layout/LayoutUtil.js
-net/miginfocom/layout/LinkHandler.js
-net/miginfocom/layout/PlatformDefaults.js
-net/miginfocom/layout/ResizeConstraint.js
-net/miginfocom/layout/UnitValue.js
-net/miginfocom/swing/MigLayout.js
-net/miginfocom/swing/SwingComponentWrapper.js
-net/miginfocom/swing/SwingContainerWrapper.js
-org/apache/xerces/jaxp/datatype/DatatypeFactoryImpl.js
-org/apache/xerces/jaxp/datatype/XMLGregorianCalendarImpl.js
-org/jmol/adapter/readers/cif/CifReader.js
-org/jmol/adapter/readers/cif/MMCifReader.js
-org/jmol/adapter/readers/pdb/PdbReader.js
-org/jmol/adapter/smarter/Atom.js
-org/jmol/adapter/smarter/AtomIterator.js
-org/jmol/adapter/smarter/AtomSetCollection.js
-org/jmol/adapter/smarter/AtomSetCollectionReader.js
-org/jmol/adapter/smarter/AtomSetObject.js
-org/jmol/adapter/smarter/Bond.js
-org/jmol/adapter/smarter/BondIterator.js
-org/jmol/adapter/smarter/Resolver.js
-org/jmol/adapter/smarter/SmarterJmolAdapter.js
-org/jmol/adapter/smarter/Structure.js
-org/jmol/adapter/smarter/StructureIterator.js
-org/jmol/adapter/smarter/XtalSymmetry.js
-org/jmol/api/AtomIndexIterator.js
-org/jmol/api/EventManager.js
-org/jmol/api/GenericFileInterface.js
-org/jmol/api/GenericMouseInterface.js
-org/jmol/api/GenericPlatform.js
-org/jmol/api/Interface.js
-org/jmol/api/JmolAdapter.js
-org/jmol/api/JmolAdapterAtomIterator.js
-org/jmol/api/JmolAdapterBondIterator.js
-org/jmol/api/JmolAdapterStructureIterator.js
-org/jmol/api/JmolCallbackListener.js
-org/jmol/api/JmolGraphicsInterface.js
-org/jmol/api/JmolMeasurementClient.js
-org/jmol/api/JmolPropertyManager.js
-org/jmol/api/JmolRendererInterface.js
-org/jmol/api/JmolRepaintManager.js
-org/jmol/api/JmolScriptEvaluator.js
-org/jmol/api/JmolScriptFunction.js
-org/jmol/api/JmolScriptManager.js
-org/jmol/api/JmolSelectionListener.js
-org/jmol/api/JmolStatusListener.js
-org/jmol/api/JmolViewer.js
-org/jmol/api/PlatformViewer.js
-org/jmol/api/SymmetryInterface.js
-org/jmol/api/Translator.js
-org/jmol/atomdata/AtomDataServer.js
-org/jmol/atomdata/RadiusData.js
-org/jmol/awt/AwtFile.js
-org/jmol/awt/AwtFont.js
-org/jmol/awt/Display.js
-org/jmol/awt/Image.js
-org/jmol/awt/Mouse.js
-org/jmol/awt/Platform.js
-org/jmol/bspt/Bspf.js
-org/jmol/bspt/Bspt.js
-org/jmol/bspt/CubeIterator.js
-org/jmol/bspt/Element.js
-org/jmol/bspt/Leaf.js
-org/jmol/bspt/Node.js
-org/jmol/c/CBK.js
-org/jmol/c/FIL.js
-org/jmol/c/PAL.js
-org/jmol/c/STER.js
-org/jmol/c/STR.js
-org/jmol/c/VDW.js
-org/jmol/g3d/CylinderRenderer.js
-org/jmol/g3d/G3DRenderer.js
-org/jmol/g3d/Graphics3D.js
-org/jmol/g3d/HermiteRenderer.js
-org/jmol/g3d/LineRenderer.js
-org/jmol/g3d/Pixelator.js
-org/jmol/g3d/Platform3D.js
-org/jmol/g3d/PrecisionRenderer.js
-org/jmol/g3d/SphereRenderer.js
-org/jmol/g3d/TextRenderer.js
-org/jmol/g3d/TextString.js
-org/jmol/g3d/TriangleRenderer.js
-org/jmol/i18n/GT.js
-org/jmol/i18n/Language.js
-org/jmol/i18n/Resource.js
-org/jmol/io/FileReader.js
-org/jmol/modelset/Atom.js
-org/jmol/modelset/AtomCollection.js
-org/jmol/modelset/AtomIteratorWithinModel.js
-org/jmol/modelset/Bond.js
-org/jmol/modelset/BondCollection.js
-org/jmol/modelset/BondIterator.js
-org/jmol/modelset/BondIteratorSelected.js
-org/jmol/modelset/Chain.js
-org/jmol/modelset/Group.js
-org/jmol/modelset/LabelToken.js
-org/jmol/modelset/Measurement.js
-org/jmol/modelset/MeasurementData.js
-org/jmol/modelset/Model.js
-org/jmol/modelset/ModelLoader.js
-org/jmol/modelset/ModelSet.js
-org/jmol/modelset/Orientation.js
-org/jmol/modelset/Structure.js
-org/jmol/modelset/Text.js
-org/jmol/modelset/TickInfo.js
-org/jmol/modelsetbio/AlphaMonomer.js
-org/jmol/modelsetbio/AlphaPolymer.js
-org/jmol/modelsetbio/AminoMonomer.js
-org/jmol/modelsetbio/AminoPolymer.js
-org/jmol/modelsetbio/BioModel.js
-org/jmol/modelsetbio/BioModelSet.js
-org/jmol/modelsetbio/BioPolymer.js
-org/jmol/modelsetbio/BioResolver.js
-org/jmol/modelsetbio/Helix.js
-org/jmol/modelsetbio/Monomer.js
-org/jmol/modelsetbio/NucleicMonomer.js
-org/jmol/modelsetbio/NucleicPolymer.js
-org/jmol/modelsetbio/PhosphorusMonomer.js
-org/jmol/modelsetbio/PhosphorusPolymer.js
-org/jmol/modelsetbio/ProteinStructure.js
-org/jmol/modelsetbio/Sheet.js
-org/jmol/render/BallsRenderer.js
-org/jmol/render/BbcageRenderer.js
-org/jmol/render/CageRenderer.js
-org/jmol/render/FontLineShapeRenderer.js
-org/jmol/render/FrankRenderer.js
-org/jmol/render/HoverRenderer.js
-org/jmol/render/LabelsRenderer.js
-org/jmol/render/MeasuresRenderer.js
-org/jmol/render/RepaintManager.js
-org/jmol/render/ShapeRenderer.js
-org/jmol/render/SticksRenderer.js
-org/jmol/render/TextRenderer.js
-org/jmol/render/UccageRenderer.js
-org/jmol/renderbio/BioShapeRenderer.js
-org/jmol/renderbio/CartoonRenderer.js
-org/jmol/renderbio/RocketsRenderer.js
-org/jmol/renderbio/StrandsRenderer.js
-org/jmol/script/ContextToken.js
-org/jmol/script/SV.js
-org/jmol/script/ScriptCompiler.js
-org/jmol/script/ScriptContext.js
-org/jmol/script/ScriptError.js
-org/jmol/script/ScriptEval.js
-org/jmol/script/ScriptExpr.js
-org/jmol/script/ScriptFlowContext.js
-org/jmol/script/ScriptFunction.js
-org/jmol/script/ScriptManager.js
-org/jmol/script/ScriptMathProcessor.js
-org/jmol/script/ScriptParam.js
-org/jmol/script/ScriptTokenParser.js
-org/jmol/script/T.js
-org/jmol/scriptext/CmdExt.js
-org/jmol/scriptext/MathExt.js
-org/jmol/scriptext/ScriptExt.js
-org/jmol/shape/AtomShape.js
-org/jmol/shape/Balls.js
-org/jmol/shape/Bbcage.js
-org/jmol/shape/FontLineShape.js
-org/jmol/shape/Frank.js
-org/jmol/shape/Hover.js
-org/jmol/shape/Labels.js
-org/jmol/shape/Measures.js
-org/jmol/shape/Mesh.js
-org/jmol/shape/Shape.js
-org/jmol/shape/Sticks.js
-org/jmol/shape/TextShape.js
-org/jmol/shape/Uccage.js
-org/jmol/shapebio/BioShape.js
-org/jmol/shapebio/BioShapeCollection.js
-org/jmol/shapebio/Cartoon.js
-org/jmol/shapebio/Rockets.js
-org/jmol/symmetry/Symmetry.js
-org/jmol/symmetry/SymmetryInfo.js
-org/jmol/symmetry/UnitCell.js
-org/jmol/thread/HoverWatcherThread.js
-org/jmol/thread/JmolThread.js
-org/jmol/util/BSUtil.js
-org/jmol/util/BoxInfo.js
-org/jmol/util/C.js
-org/jmol/util/ColorEncoder.js
-org/jmol/util/CommandHistory.js
-org/jmol/util/DefaultLogger.js
-org/jmol/util/Edge.js
-org/jmol/util/Elements.js
-org/jmol/util/Escape.js
-org/jmol/util/GData.js
-org/jmol/util/Geodesic.js
-org/jmol/util/Int2IntHash.js
-org/jmol/util/Int2IntHashEntry.js
-org/jmol/util/Logger.js
-org/jmol/util/LoggerInterface.js
-org/jmol/util/MeshSurface.js
-org/jmol/util/Node.js
-org/jmol/util/Normix.js
-org/jmol/util/Point3fi.js
-org/jmol/util/Rectangle.js
-org/jmol/util/Rgb16.js
-org/jmol/util/Shader.js
-org/jmol/util/SimpleEdge.js
-org/jmol/util/SimpleNode.js
-org/jmol/util/SimpleUnitCell.js
-org/jmol/util/TempArray.js
-org/jmol/viewer/ActionManager.js
-org/jmol/viewer/AnimationManager.js
-org/jmol/viewer/ColorManager.js
-org/jmol/viewer/FileManager.js
-org/jmol/viewer/Gesture.js
-org/jmol/viewer/GlobalSettings.js
-org/jmol/viewer/JC.js
-org/jmol/viewer/ModelManager.js
-org/jmol/viewer/MotionPoint.js
-org/jmol/viewer/MouseState.js
-org/jmol/viewer/PropertyManager.js
-org/jmol/viewer/SelectionManager.js
-org/jmol/viewer/ShapeManager.js
-org/jmol/viewer/StateManager.js
-org/jmol/viewer/StatusManager.js
-org/jmol/viewer/TransformManager.js
-org/jmol/viewer/Viewer.js
-org/jmol/viewer/binding/Binding.js
-org/jmol/viewer/binding/JmolBinding.js
-org/xml/sax/AttributeList.js
-org/xml/sax/Attributes.js
-org/xml/sax/ContentHandler.js
-org/xml/sax/InputSource.js
-org/xml/sax/Parser.js
-org/xml/sax/XMLReader.js
-org/xml/sax/ext/Attributes2.js
-sun/awt/AWTAccessor.js
-sun/awt/CausedFocusEvent.js
-sun/awt/EventQueueItem.js
-sun/awt/KeyboardFocusManagerPeerProvider.js
-sun/awt/SunGraphicsCallback.js
-sun/awt/image/DataStealer.js
-sun/awt/image/IntegerComponentRaster.js
-sun/awt/image/IntegerInterleavedRaster.js
-sun/awt/image/OffScreenImageSource.js
-sun/awt/image/SunWritableRaster.js
-sun/font/AttributeValues.js
-sun/font/EAttribute.js
-sun/font/FontDesignMetrics.js
-sun/java2d/StateTrackable.js
-sun/java2d/StateTrackableDelegate.js
-sun/nio/cs/ArrayDecoder.js
-sun/nio/cs/HistoricallyNamedCharset.js
-sun/nio/cs/StandardCharsets.js
-sun/nio/cs/ThreadLocalCoders.js
-sun/nio/cs/UTF_8.js
-sun/nio/cs/Unicode.js
-sun/swing/DefaultLookup.js
-sun/swing/StringUIClientPropertyKey.js
-sun/swing/SwingUtilities2.js
-sun/swing/UIAction.js
-sun/swing/UIClientPropertyKey.js
-sun/swing/table/DefaultTableCellHeaderRenderer.js
-sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
-sun/util/calendar/AbstractCalendar.js
-sun/util/calendar/BaseCalendar.js
-sun/util/calendar/CalendarDate.js
-sun/util/calendar/CalendarSystem.js
-sun/util/calendar/CalendarUtils.js
-sun/util/calendar/Gregorian.js
-sun/util/calendar/ZoneInfo.js
-sun/util/resources/LocaleData.js
-swingjs/JSAbstractDocument.js
-swingjs/JSCharSet.js
-swingjs/JSDnD.js
-swingjs/JSDocumentEvent.js
-swingjs/JSFocusPeer.js
-swingjs/JSGraphicsCompositor.js
-swingjs/JSImage.js
-swingjs/JSImagekit.js
-swingjs/JSKeyEvent.js
-swingjs/JSMenuManager.js
-swingjs/JSPlainDocument.js
-swingjs/JSTempFile.js
-swingjs/a2s/A2SContainer.js
-swingjs/a2s/Dialog.js
-swingjs/api/JSMinimalAbstractDocument.js
-swingjs/json/JSON.js
-swingjs/jzlib/Adler32.js
-swingjs/jzlib/CRC32.js
-swingjs/jzlib/Checksum.js
-swingjs/jzlib/InfBlocks.js
-swingjs/jzlib/InfCodes.js
-swingjs/jzlib/InfTree.js
-swingjs/jzlib/Inflate.js
-swingjs/jzlib/Inflater.js
-swingjs/jzlib/InflaterInputStream.js
-swingjs/jzlib/ZStream.js
-swingjs/plaf/BasicArrowButton.js
-swingjs/plaf/BasicHTML.js
-swingjs/plaf/CellHolder.js
-swingjs/plaf/CenterLayout.js
-swingjs/plaf/JSAppletUI.js
-swingjs/plaf/JSColorChooserUI.js
-swingjs/plaf/JSDesktopIconUI.js
-swingjs/plaf/JSDesktopPaneUI.js
-swingjs/plaf/JSDialogUI.js
-swingjs/plaf/JSFormattedTextFieldUI.js
-swingjs/plaf/JSGraphicsUtils.js
-swingjs/plaf/JSInternalFrameUI.js
-swingjs/plaf/JSListUI.js
-swingjs/plaf/JSPopupUI.js
-swingjs/plaf/JSProgressBarUI.js
-swingjs/plaf/JSScrollPaneUI.js
-swingjs/plaf/JSSpinnerUI.js
-swingjs/plaf/JSTabbedPaneUI.js
-swingjs/plaf/JSTableHeaderUI.js
-swingjs/plaf/JSTableUI.js
-swingjs/plaf/JSTextAreaUI.js
-swingjs/plaf/JSTextFieldUI.js
-swingjs/plaf/JSTextUI.js
-swingjs/plaf/JSTextViewUI.js
-swingjs/plaf/JSToolTipUI.js
-swingjs/plaf/JSViewportUI.js
-swingjs/plaf/TextListener.js
-swingjs/xml/JSJAXBClass.js
-swingjs/xml/JSJAXBContext.js
-swingjs/xml/JSJAXBContextFactory.js
-swingjs/xml/JSJAXBDatatypeFactory.js
-swingjs/xml/JSJAXBField.js
-swingjs/xml/JSJAXBUnmarshaller.js
-swingjs/xml/JSSAXAttributes.js
-swingjs/xml/JSSAXParser.js
-swingjs/xml/JSXMLGregorianCalendarImpl.js
-swingjs/xml/JSXMLInputFactory.js
-swingjs/xml/JSXMLStreamReader.jscom/stevesoft/pat/Any.js
-swingjs/xml/JSXMLStreamReader.jscom/stevesoft/pat/Boundary.js
diff --git a/utils/jalviewjs/classlists/jvjmol.txt b/utils/jalviewjs/classlists/jvjmol.txt
new file mode 100644 (file)
index 0000000..bae181d
--- /dev/null
@@ -0,0 +1,232 @@
+jalview/ext/jmol/JalviewJmolBinding.js
+jalview/ext/jmol/JmolCommands.js
+jalview/ext/jmol/JmolParser.js
+jalview/gui/AppJmol.js
+jalview/gui/AppJmolBinding.js
+jalview/gui/StructureViewerBase.js
+jalview/io/StructureFile.js
+jalview/jbgui/GStructureViewer.js
+jalview/renderer/seqfeatures/FeatureColourFinder.js
+jalview/structure/StructureListener.js
+jalview/structure/StructureMapping.js
+jalview/structure/StructureMappingcommandSet.js
+jalview/structures/models/AAStructureBindingModel.js
+jalview/structures/models/SequenceStructureBindingModel.js
+jalview/util/CaseInsensitiveString.js
+jalview/util/HttpUtils.js
+jalview/util/MapList.js
+jalview/ws/dbsources/EbiFileRetrievedProxy.js
+jalview/ws/dbsources/Pdb.js
+jalview/ws/seqfetcher/DbSourceProxy.js
+jalview/ws/seqfetcher/DbSourceProxyImpl.js
+java/awt/font/TextAttribute.js
+java/awt/image/ImageProducer.js
+java/awt/image/PixelGrabber.js
+java/io/BufferedWriter.js
+java/io/FilterOutputStream.js
+java/io/OutputStream.js
+java/io/OutputStreamWriter.js
+java/io/PrintStream.js
+java/io/StringWriter.js
+java/io/Writer.js
+javajs/api/BytePoster.js
+javajs/api/GenericOutputChannel.js
+javajs/util/A4.js
+javajs/util/BS.js
+javajs/util/CU.js
+javajs/util/DF.js
+javajs/util/LimitedLineReader.js
+javajs/util/M3.js
+javajs/util/M34.js
+javajs/util/M4.js
+javajs/util/Measure.js
+javajs/util/OC.js
+javajs/util/P3.js
+javajs/util/P3i.js
+javajs/util/P4.js
+javajs/util/T3.js
+javajs/util/T3i.js
+javajs/util/T4.js
+javajs/util/V3.js
+mc_view/Atom.js
+mc_view/Bond.js
+mc_view/PDBChain.js
+mc_view/Residue.js
+org/jmol/adapter/readers/pdb/PdbReader.js
+org/jmol/adapter/smarter/Atom.js
+org/jmol/adapter/smarter/AtomIterator.js
+org/jmol/adapter/smarter/AtomSetCollection.js
+org/jmol/adapter/smarter/AtomSetCollectionReader.js
+org/jmol/adapter/smarter/AtomSetObject.js
+org/jmol/adapter/smarter/Bond.js
+org/jmol/adapter/smarter/BondIterator.js
+org/jmol/adapter/smarter/Resolver.js
+org/jmol/adapter/smarter/SmarterJmolAdapter.js
+org/jmol/adapter/smarter/Structure.js
+org/jmol/api/AtomIndexIterator.js
+org/jmol/api/EventManager.js
+org/jmol/api/GenericFileInterface.js
+org/jmol/api/GenericMouseInterface.js
+org/jmol/api/GenericPlatform.js
+org/jmol/api/Interface.js
+org/jmol/api/JmolAdapter.js
+org/jmol/api/JmolAdapterAtomIterator.js
+org/jmol/api/JmolAdapterBondIterator.js
+org/jmol/api/JmolCallbackListener.js
+org/jmol/api/JmolGraphicsInterface.js
+org/jmol/api/JmolPropertyManager.js
+org/jmol/api/JmolRendererInterface.js
+org/jmol/api/JmolRepaintManager.js
+org/jmol/api/JmolScriptEvaluator.js
+org/jmol/api/JmolScriptFunction.js
+org/jmol/api/JmolScriptManager.js
+org/jmol/api/JmolSelectionListener.js
+org/jmol/api/JmolStatusListener.js
+org/jmol/api/JmolViewer.js
+org/jmol/api/PlatformViewer.js
+org/jmol/api/Translator.js
+org/jmol/atomdata/AtomDataServer.js
+org/jmol/atomdata/RadiusData.js
+org/jmol/awt/AwtFile.js
+org/jmol/awt/AwtFont.js
+org/jmol/awt/Display.js
+org/jmol/awt/Image.js
+org/jmol/awt/Mouse.js
+org/jmol/awt/Platform.js
+org/jmol/bspt/Bspf.js
+org/jmol/bspt/Bspt.js
+org/jmol/bspt/CubeIterator.js
+org/jmol/bspt/Element.js
+org/jmol/bspt/Leaf.js
+org/jmol/bspt/Node.js
+org/jmol/c/CBK.js
+org/jmol/c/FIL.js
+org/jmol/c/PAL.js
+org/jmol/c/STER.js
+org/jmol/c/STR.js
+org/jmol/c/VDW.js
+org/jmol/g3d/CylinderRenderer.js
+org/jmol/g3d/G3DRenderer.js
+org/jmol/g3d/Graphics3D.js
+org/jmol/g3d/HermiteRenderer.js
+org/jmol/g3d/LineRenderer.js
+org/jmol/g3d/Pixelator.js
+org/jmol/g3d/Platform3D.js
+org/jmol/g3d/PrecisionRenderer.js
+org/jmol/g3d/SphereRenderer.js
+org/jmol/g3d/TextRenderer.js
+org/jmol/g3d/TextString.js
+org/jmol/g3d/TriangleRenderer.js
+org/jmol/i18n/GT.js
+org/jmol/i18n/Language.js
+org/jmol/i18n/Resource.js
+org/jmol/io/FileReader.js
+org/jmol/modelset/Atom.js
+org/jmol/modelset/AtomCollection.js
+org/jmol/modelset/AtomIteratorWithinModel.js
+org/jmol/modelset/Bond.js
+org/jmol/modelset/BondCollection.js
+org/jmol/modelset/BondIterator.js
+org/jmol/modelset/BondIteratorSelected.js
+org/jmol/modelset/Chain.js
+org/jmol/modelset/Group.js
+org/jmol/modelset/Model.js
+org/jmol/modelset/ModelLoader.js
+org/jmol/modelset/ModelSet.js
+org/jmol/modelset/Orientation.js
+org/jmol/modelset/Structure.js
+org/jmol/modelsetbio/AlphaMonomer.js
+org/jmol/modelsetbio/AlphaPolymer.js
+org/jmol/modelsetbio/BioModel.js
+org/jmol/modelsetbio/BioModelSet.js
+org/jmol/modelsetbio/BioPolymer.js
+org/jmol/modelsetbio/BioResolver.js
+org/jmol/modelsetbio/Helix.js
+org/jmol/modelsetbio/Monomer.js
+org/jmol/modelsetbio/ProteinStructure.js
+org/jmol/render/BallsRenderer.js
+org/jmol/render/FontLineShapeRenderer.js
+org/jmol/render/FrankRenderer.js
+org/jmol/render/RepaintManager.js
+org/jmol/render/ShapeRenderer.js
+org/jmol/render/SticksRenderer.js
+org/jmol/renderbio/BackboneRenderer.js
+org/jmol/renderbio/BioShapeRenderer.js
+org/jmol/renderbio/CartoonRenderer.js
+org/jmol/renderbio/RocketsRenderer.js
+org/jmol/renderbio/StrandsRenderer.js
+org/jmol/script/ContextToken.js
+org/jmol/script/SV.js
+org/jmol/script/ScriptCompiler.js
+org/jmol/script/ScriptContext.js
+org/jmol/script/ScriptError.js
+org/jmol/script/ScriptEval.js
+org/jmol/script/ScriptExpr.js
+org/jmol/script/ScriptFlowContext.js
+org/jmol/script/ScriptFunction.js
+org/jmol/script/ScriptManager.js
+org/jmol/script/ScriptMathProcessor.js
+org/jmol/script/ScriptParam.js
+org/jmol/script/ScriptTokenParser.js
+org/jmol/script/T.js
+org/jmol/scriptext/MathExt.js
+org/jmol/shape/AtomShape.js
+org/jmol/shape/Balls.js
+org/jmol/shape/Frank.js
+org/jmol/shape/Mesh.js
+org/jmol/shape/Shape.js
+org/jmol/shape/Sticks.js
+org/jmol/shapebio/Backbone.js
+org/jmol/shapebio/BioShape.js
+org/jmol/shapebio/BioShapeCollection.js
+org/jmol/shapebio/Cartoon.js
+org/jmol/shapebio/Rockets.js
+org/jmol/thread/HoverWatcherThread.js
+org/jmol/thread/JmolThread.js
+org/jmol/util/BSUtil.js
+org/jmol/util/BoxInfo.js
+org/jmol/util/C.js
+org/jmol/util/ColorEncoder.js
+org/jmol/util/CommandHistory.js
+org/jmol/util/DefaultLogger.js
+org/jmol/util/Edge.js
+org/jmol/util/Elements.js
+org/jmol/util/Escape.js
+org/jmol/util/GData.js
+org/jmol/util/Geodesic.js
+org/jmol/util/Int2IntHash.js
+org/jmol/util/Int2IntHashEntry.js
+org/jmol/util/Logger.js
+org/jmol/util/LoggerInterface.js
+org/jmol/util/MeshSurface.js
+org/jmol/util/Node.js
+org/jmol/util/Normix.js
+org/jmol/util/Point3fi.js
+org/jmol/util/Rectangle.js
+org/jmol/util/Rgb16.js
+org/jmol/util/Shader.js
+org/jmol/util/SimpleEdge.js
+org/jmol/util/SimpleNode.js
+org/jmol/util/TempArray.js
+org/jmol/viewer/ActionManager.js
+org/jmol/viewer/AnimationManager.js
+org/jmol/viewer/ColorManager.js
+org/jmol/viewer/FileManager.js
+org/jmol/viewer/Gesture.js
+org/jmol/viewer/GlobalSettings.js
+org/jmol/viewer/JC.js
+org/jmol/viewer/ModelManager.js
+org/jmol/viewer/MotionPoint.js
+org/jmol/viewer/MouseState.js
+org/jmol/viewer/PropertyManager.js
+org/jmol/viewer/SelectionManager.js
+org/jmol/viewer/ShapeManager.js
+org/jmol/viewer/StateManager.js
+org/jmol/viewer/StatusManager.js
+org/jmol/viewer/TransformManager.js
+org/jmol/viewer/Viewer.js
+org/jmol/viewer/binding/Binding.js
+org/jmol/viewer/binding/JmolBinding.js
+sun/awt/image/OffScreenImageSource.js
+sun/font/AttributeValues.js
+sun/font/EAttribute.js
\ No newline at end of file
diff --git a/utils/jalviewjs/classlists/stevesoft.txt b/utils/jalviewjs/classlists/stevesoft.txt
new file mode 100644 (file)
index 0000000..e8f1747
--- /dev/null
@@ -0,0 +1,105 @@
+com/stevesoft/pat/Multi.js
+com/stevesoft/pat/NotImplementedError.js
+com/stevesoft/pat/FileRegex.js
+com/stevesoft/pat/Validator.js
+com/stevesoft/pat/UnicodeCurrency.js
+com/stevesoft/pat/End.js
+com/stevesoft/pat/Ctrl.js
+com/stevesoft/pat/PopRule.js
+com/stevesoft/pat/UnicodePunct.js
+com/stevesoft/pat/StringLike.js
+com/stevesoft/pat/StrPos.js
+com/stevesoft/pat/Skip.js
+com/stevesoft/pat/Branch.js
+com/stevesoft/pat/Transformer.js
+com/stevesoft/pat/RegexReader.js
+com/stevesoft/pat/NUnicodeW.js
+com/stevesoft/pat/BackMatch.js
+com/stevesoft/pat/parsePerl.js
+com/stevesoft/pat/Skipped.js
+com/stevesoft/pat/patInf.js
+com/stevesoft/pat/CodeVal.js
+com/stevesoft/pat/oneChar.js
+com/stevesoft/pat/Replacer.js
+com/stevesoft/pat/wrap/CharArrayBufferWrap.js
+com/stevesoft/pat/wrap/CharArrayWrap.js
+com/stevesoft/pat/wrap/WriterWrap.js
+com/stevesoft/pat/wrap/StringWrap.js
+com/stevesoft/pat/wrap/RandomAccessFileWrap.js
+com/stevesoft/pat/wrap/StringBufferWrap.js
+com/stevesoft/pat/FastMulti.js
+com/stevesoft/pat/TransRepRule.js
+com/stevesoft/pat/RBuffer.js
+com/stevesoft/pat/MultiMin.js
+com/stevesoft/pat/lookAhead.js
+com/stevesoft/pat/SpecialRule.js
+com/stevesoft/pat/PushRule.js
+com/stevesoft/pat/FastChar.js
+com/stevesoft/pat/Multi_stage2.js
+com/stevesoft/pat/RegexTokenizer.js
+com/stevesoft/pat/NUnicodeDigit.js
+com/stevesoft/pat/BackG.js
+com/stevesoft/pat/BadRangeArgs.js
+com/stevesoft/pat/NullRule.js
+com/stevesoft/pat/NUnicodePunct.js
+com/stevesoft/pat/UnicodeDigit.js
+com/stevesoft/pat/UniValidator.js
+com/stevesoft/pat/Or.js
+com/stevesoft/pat/Custom.js
+com/stevesoft/pat/UnicodeW.js
+com/stevesoft/pat/DirFileRegex.js
+com/stevesoft/pat/RegHolder.js
+com/stevesoft/pat/RegRes.js
+com/stevesoft/pat/Bits.js
+com/stevesoft/pat/UnicodeMath.js
+com/stevesoft/pat/patInt.js
+com/stevesoft/pat/RegSyntax.js
+com/stevesoft/pat/Backup.js
+com/stevesoft/pat/TransPat.js
+com/stevesoft/pat/NullPattern.js
+com/stevesoft/pat/OrMark.js
+com/stevesoft/pat/NonDirFileRegex.js
+com/stevesoft/pat/ChangeRule.js
+com/stevesoft/pat/NoPattern.js
+com/stevesoft/pat/Boundary.js
+com/stevesoft/pat/RuleHolder.js
+com/stevesoft/pat/RightRule.js
+com/stevesoft/pat/CaseMgr.js
+com/stevesoft/pat/CustomEndpoint.js
+com/stevesoft/pat/NUnicodeMath.js
+com/stevesoft/pat/DotMulti.js
+com/stevesoft/pat/Bracket.js
+com/stevesoft/pat/Start.js
+com/stevesoft/pat/Any.js
+com/stevesoft/pat/UnicodeLower.js
+com/stevesoft/pat/PatternSub.js
+com/stevesoft/pat/WantMoreTextReplaceRule.js
+com/stevesoft/pat/Pthings.js
+com/stevesoft/pat/Regex.js
+com/stevesoft/pat/Group.js
+com/stevesoft/pat/UnicodeUpper.js
+com/stevesoft/pat/Skip2.js
+com/stevesoft/pat/RegexWriter.js
+com/stevesoft/pat/UnicodeWhite.js
+com/stevesoft/pat/Pattern.js
+com/stevesoft/pat/LeftRule.js
+com/stevesoft/pat/Range.js
+com/stevesoft/pat/RegOpt.js
+com/stevesoft/pat/PartialBuffer.js
+com/stevesoft/pat/UnicodeAlpha.js
+com/stevesoft/pat/Rthings.js
+com/stevesoft/pat/Prop.js
+com/stevesoft/pat/NUnicodeCurrency.js
+com/stevesoft/pat/BackRefRule.js
+com/stevesoft/pat/SubMark.js
+com/stevesoft/pat/NUnicodeAlpha.js
+com/stevesoft/pat/FastBracket.js
+com/stevesoft/pat/NUnicodeWhite.js
+com/stevesoft/pat/StringBufferLike.js
+com/stevesoft/pat/RegSyntaxError.js
+com/stevesoft/pat/BasicStringBufferLike.js
+com/stevesoft/pat/AmpersandRule.js
+com/stevesoft/pat/StringRule.js
+com/stevesoft/pat/ReplaceRule.js
+com/stevesoft/pat/CodeRule.js
+com/stevesoft/pat/SkipBMH.js
index 94bd28e..26d7abe 100644 (file)
Binary files a/utils/jalviewjs/libjs/jmol-app.zip and b/utils/jalviewjs/libjs/jmol-app.zip differ
diff --git a/utils/jalviewjs/site-resources/___j2sflags.htm b/utils/jalviewjs/site-resources/___j2sflags.htm
new file mode 100644 (file)
index 0000000..62a786d
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head><title>URL command line flags for SwingJS</title></head>
+<body><h3>URL command-line arguments for SwingJS</h3>
+<br>To enable these flags, simply add them after # or ? on your page.
+<br>The test is very simple -- just a case-sensitive string check. 
+<br>Separate them with &amp; For example:
+<br><a href="test_Test_Class.html?j2sverbose&j2snozcore&j2strace=applet">test_Test_Class.html?j2sverbose&j2snozcore&j2strace=applet</a>
+<br><br><table width=800 border=1 cellpadding=5>
+<tr><th colspan="2"></th></tr>
+<tr><td>j2sargs=a|b|c</td><td>arguments to be passed on to application.main(); use "|" to separate arguments. Overrides Info.args, which should be an array of strings, if present</td></tr>
+<tr><td>j2sdebugclip</td><td>shows all show/restore and clip operations in JSGraphics2D</td></tr>
+<tr><td>j2sdebugcode</td><td>deprecated; see j2snocore</td></tr>
+<tr><td>j2sdebugcore</td><td>deprecated; see j2snozcore</td></tr>
+<tr><td>j2sdebugpaint</td><td>report repaint manager information</td></tr>
+<tr><td>j2sevents</td><td>report ComponentEvent instances</td></tr>
+<tr><td>j2sfilter=xxx</td><td>remove messages with the specified text from System.out</td></tr>
+<tr><td>j2sheadless</td><td>run headlessly (must be a main()-based application or library without Swing or AWT)</td></tr>
+<tr><td>j2slang=en_US</td><td>default language for java.util.Locale (overrides Info.language)</td></tr>
+<tr><td>j2smouse</td><td>report mouse events other than mousemove</td></tr>
+<tr><td>j2smousemove</td><td>report all mouse events, including mousemove</td></tr>
+<tr><td>j2snocore</td><td>do not load core files (from j2s/core/)</td></tr>
+<tr><td>j2snoeval</td><td>use new Function() instead of eval(); breaks debugging, experimental</td></tr>
+<tr><td>j2snooutput</td><td>report only System.err message, not  System.out </td></tr>
+<tr><td>j2snozcore</td><td>use the uncompressed j2s/core/xxxcore.js files, not the compressed core.z.js files</td></tr>
+<tr><td>j2sprofile</td><td>track object creation; use J2S.getProfile() when you want a report; J2S.getProfile() or J2S.getProfile(nsec) to restart profiling anytime.</td></tr>
+<tr><td>j2sstrict</td><td>strict mode -- experimental</td></tr>
+<tr><td>j2strace=xxx or j2strace="xxx"</td><td>throw up an alert in the browser and a debugger statement in the developer whenever the specified text is found in System.out or System.err; if quotes are used, this must be an exact match to the entire output text (particularly useful when the message is something like "0", which otherwise would be next to impossible to find.</td></tr>
+<tr><td>j2sverbose</td><td>report all files loaded using AJAX</td></tr>
+</table>
+</body>
+</html>
@@ -5,6 +5,8 @@
 <script src="swingjs/swingjs2.js"></script>
 <script>
 
+// NOTE: The applet on this page is "Jalview". 
+
 // BH 2019.10.06 adds Tree and Pca functionality
 // BH see issue JAL-3451
 
@@ -24,12 +26,13 @@ Info = {
 jvGet = function(what) {
        switch(what) {
        case "tree":
-               testApplet.app.openTreePanel$jalview_gui_AlignFrame$S$S(null, "NJ","BLOSUM62")
+               Jalview.app.openTreePanel("NJ","BLOSUM62")
                break;
        case "pca":
-               testApplet.app.openPcaPanel$jalview_gui_AlignFrame$S(null, "BLOSUM62")
+               Jalview.app.openPcaPanel("BLOSUM62")
                break;
        case "3D":
+               Jalview.app.showStructure("1a70", "mmcif");
                break;
        }
        
@@ -37,7 +40,7 @@ jvGet = function(what) {
 
 $(document).ready(function() {
 
-  SwingJS.getApplet('testApplet', Info);
+  SwingJS.getApplet('Jalview', Info);
 
 });
 
@@ -60,7 +63,7 @@ The basic idea is that we have something interesting to say &mdash; some sort of
 across to our visitors with more than just text and images. The idea is to have a <b>dynamic</b> page that will involve <b>user interaction</b>. 
 <br><br>
 We start with a Jalview desktop. You can't see it, because I have placed it in a <code>div</code> tag with style <i>width:0px;height:0px</i> just after the period that ends this sentence.
-<div id="jalview-desktop-div" style="width:0px;height:0px;"></div>
+<div id="Jalview-desktop-div" style="width:0px;height:0px;"></div>
 <br>
 The idea is NOT to teach visitors how to use Jalview. The idea is to seamlessly integrate components of Jalview that can be used to enrich a discussion. 
 Like JSmol in <a target="_blank" href="http://proteopedia.org/wiki/index.php/Main_Page"><img src=https://pbs.twimg.com/profile_images/818051034/proteopedia_135x200_small_logo_for_Twitter_400x400.png width=16 height=16/>Proteopedia</a>.
@@ -71,7 +74,7 @@ See the big block of red color? That's the <i>Ferredoxin fold</i>domain.
 
 </div>
 </td><td style="background-color:lightgray;padding:20px">
-<div id="jalview-alignment-div" style="padding:20px;position:relative;top:0px;left:0px;width:680px;height:400px">
+<div id="Jalview-alignment-div" style="padding:20px;position:relative;top:0px;left:0px;width:680px;height:400px">
 <br><br>
 The alignment frame will appear here momentarily. When it does, you can go ahead and manipulate the alignment with your mouse.
 </div>
@@ -85,24 +88,27 @@ The alignment frame will appear here momentarily. When it does, you can go ahead
 <table style="background-color:lightgray"><tr><td  style="padding:0px 0px 0px 20px">
 <b>Select a few alignments</b> by left-dragging across a few rows of the alignment to make a selection box. 
 Then click one of the buttons below to see more information about your selected subset of the alignment.
-<ul>
-<li><button onclick='jvGet("tree")'>similarity tree</button></li>
-<li><button onclick='jvGet("pca")'>principal component analysis</button></li>
-</ul>
-
+<br><br>
+<button onclick='jvGet("tree")'>similarity tree</button> <button onclick='jvGet("pca")'>principal component analysis</button>
+<br><br>
+One more thing. Let's take a <a href="javascript:jvGet('3D')" style="text-decoration:none;color:red">look at the 3D structure</a> of one these proteins. Ferredoxins are important, because they have 
+iron-sulfur clusters that can accept and deliver electrons in metabolic processes. Let's see if we can find it. 
+<br><br>
 
 
 
 </td><td style="padding:0px 0px 0px 0px">
 <table style="border-spacing:0"><tr><td style="background-color:lightgray;padding:10px 0px 10px 20px">
-<div id="jalview-tree-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
+<div id="Jalview-tree-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
 <br><br><br><br>
-jalview-tree-div
+<a href="javascript:jvGet('tree')">Jalview-tree-div</a>
 </div>
 </td><td style="background-color:lightgray;padding:10px 20px 10px 0px">
-<div id="jalview-pca-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
+<div id="Jalview-pca-div" style="position:relative;top:0px;left:0px;width:500px;height:500px">
 <br><br><br><br>
-jalview-pca-div
+<a href="javascript:jvGet('pca')">Jalview-pca-div</a>
+<br><br>
+
 </div>
 </td></tr></table>
 
@@ -111,19 +117,23 @@ jalview-pca-div
 
 </td></tr>
 
+<!-- let it float
 <tr>
 
 <td valign=top style="padding:20px;height:650px" >
 
-<div id="jalview-structureviewer-div" style="position:relative;top:0px;left:0px;width:600px;height:600px">
-jalview-strucddtureviewer-div
-</div>
+<div id="Jalview-structureviewer-div" style="position:relative;top:0px;left:0px;width:600px;height:600px">
+<a href="javascript:jvGet('3D')">Jalview-structureviewer-div</a>
+</div> 
 </td>
 <td valign=top style="padding:20px;background-color:white" >
 One more thing. Let's take a look at the 3D structure of one these proteins. Ferredoxins are important, because they have 
 iron-sulfur clusters that can accept and deliver electrons in metabolic processes. Let's see if we can find it. 
 <br><center><button onclick='jvGet("3D")'>add the 3D structure</button>
-</td></tr></table>
+</td></tr>
+ -->
+
+</table>
 
 
 <!-- debugging (hidden) -->
diff --git a/utils/jalviewjs/site-resources/_jalview_embedded_example2.html b/utils/jalviewjs/site-resources/_jalview_embedded_example2.html
new file mode 100644 (file)
index 0000000..89a4f71
--- /dev/null
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded JalviewJS Example 2</title><meta charset="utf-8" />
+<script src="swingjs/swingjs2.js"></script>
+<script>
+
+// NOTE: The applet on this page is "Jalview". 
+
+// BH 2019.10.06 adds Tree and Pca functionality
+// BH see issue JAL-3451
+
+if (!self.SwingJS)alert('swingjs2.js was not found. It needs to be in swingjs folder in the same directory as ' + document.location.href)
+Info = {
+  code: null,
+  main: "jalview.bin.JalviewJS2",
+  core: "NONE",
+//     core:"_jalview",
+
+       oninit:function() {$("#links").show();},
+       noannotation: true,
+       
+  readyFunction: null,
+       serverURL: 'https://chemapps.stolaf.edu/jmol/jsmol/php/jsmol.php',
+       j2sPath: 'swingjs/j2s',
+       console:'sysoutdiv',
+       allowjavascript: true
+}
+
+jvGet = function(what) {
+       switch(what) {
+       case "select1":
+               alert(Jalview1.getApp().getSelectedSequences());
+               break;
+       case "select2":
+               alert(Jalview2.getApp().getSelectedSequences());        
+               break;
+       case "select1fasta":
+               alert(Jalview1.getApp().getSelectedSequencesAsAlignment("fasta",true));
+               break;
+       case "select2fasta":
+               alert(Jalview2.getApp().getSelectedSequencesAsAlignment("fasta",true));
+               break;
+       }
+       
+}
+
+$(document).ready(function() {
+
+         SwingJS.getApplet('Jalview1', Info);
+         SwingJS.getApplet('Jalview2', Info);
+
+});
+
+</script>
+</head>
+<body style="background-image: url(images/coolVeryLightBG.png);">
+<table style="width:1000px;border:2px solid lightblue;border-spacing:0;font-size:16pt;" padding="10" valign="top">
+<tr>
+<td style="font-size:24;font-weight:bold;background-color:lightblue" colspan=2><center>Demonstration of embedded JalviewJS components</center>
+</td>
+
+
+</tr><tr>
+
+
+<td colspan=2 valign=top style="padding:20px;background-color:lightgray">
+this page tests two Jalview apps on the same page.
+<div id="Jalview1-desktop-div" style="width:0px;height:0px;"></div>
+<div id="Jalview2-desktop-div" style="width:0px;height:0px;"></div>
+</td></tr>
+<tr><td style="background-color:lightgray;padding:10px">
+<div id="Jalview1-alignment-div" style="padding:10px;position:relative;width:550px;height:300px">
+</td>
+<td style="background-color:lightgray;padding:10px">
+<div id="Jalview2-alignment-div" style="padding:10px;position:relative;width:550px;height:300px">
+</td>
+</tr>
+<tr><td>
+<div style="display:block;width:500px;height:300px;">
+<div id="sysoutdiv" style="border:1px solid green;width:100%;height:95%;overflow:auto"></div>
+This is System.out. <a href="javascript:J2S.thisApplet._clearConsole()">clear it</a> <br>Add ?j2snocore to URL to see full class list; ?j2sdebug to use uncompressed j2s/core files <br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
+</div>
+
+</td><td valign=top>
+<div id=links style="display:none">
+<a href="javascript:jvGet('select1')">Show Jalview1 selections</a>
+
+<a href="javascript:jvGet('select2')">Show Jalview2 selections</a>
+
+<br>
+<a href="javascript:jvGet('select1fasta')">Show Jalview1 selections (fasta)</a>
+
+<a href="javascript:jvGet('select2fasta')">Show Jalview2 selections (fasta)</a>
+</div>
+</td></tr>
+</table>
+
+
+</body>
+</html>
index 7c20d44..d132460 100644 (file)
@@ -26,7 +26,7 @@ getClassList = function(){J2S._saveFile('_j2sclasslist.txt', Clazz.ClassFilesLoa
 </script>
 <div style="position:absolute;left:900px;top:30px;width:600px;height:300px;">
 <div id="sysoutdiv" contentEditable="true" style="border:1px solid green;width:100%;height:95%;overflow:auto"></div>
-This is System.out. <a href="javascript:testApplet._clearConsole()">clear it</a> <br>Add ?j2snocore to URL to see full class list; ?j2sdebug to use uncompressed j2s/core files <br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
+This is System.out. <a href="javascript:testApplet._clearConsole()">clear it</a>  <a href='javascript:J2S.getProfile()'>start/stop profiling</a><br>see <a href=___j2sflags.htm>___j2sflags.htm</a> for SwingJS URL command-line options<br><a href="javascript:getClassList()">get _j2sClassList.txt</a>
 </div>
 </body>
 </html>