From: BobHanson Date: Thu, 11 Jun 2020 12:13:36 +0000 (-0500) Subject: Merge branch 'Jalview-JS/develop.JAL-3446.ctrlDown' into X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=ae737f35e5185aa421563e4c061340a3c4152514;hp=d7111d39548c57836f77bd4403bf2d124fd78820;p=jalview.git Merge branch 'Jalview-JS/develop.JAL-3446.ctrlDown' into Jalview-JS/develop Conflicts: src/jalview/bin/JalviewJS2.java src/jalview/gui/Desktop.java src/jalview/util/Platform.java --- diff --git a/build.gradle b/build.gradle index e4ffa8a..0be087f 100644 --- a/build.gradle +++ b/build.gradle @@ -12,13 +12,25 @@ 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 { mavenCentral() mavenLocal() } + dependencies { + classpath "com.vladsch.flexmark:flexmark-all:0.62.0" + } } @@ -992,22 +1004,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 } } @@ -1046,47 +1061,110 @@ 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, "") + + 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 = ''' + + + + + + +''' + htmlText += ((title != null) ? " ${title}" : '' ) + htmlText += ''' + +''' + htmlText += ((cssFile != null) ? cssFile.text : '') + htmlText += ''' + +''' + htmlText += htmlBody + htmlText += ''' + + +''' + + def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html") + def htmlFile = file(htmlFilePath) + htmlFile.text = htmlText + } +} + + +task convertMdFiles { dependsOn cleanBuildingHTML - def buildingMD = "${jalviewDir}/${doc_dir}/building.md" - def css = "${jalviewDir}/${doc_dir}/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 + dependsOn convertMdFiles def syncDir = "${classesDir}/${doc_dir}" from fileTree("${jalviewDir}/${doc_dir}") into syncDir - } @@ -1309,6 +1387,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 } @@ -1783,7 +1862,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] diff --git a/doc/JalviewJS-API.md b/doc/JalviewJS-API.md new file mode 100644 index 0000000..64cae62 --- /dev/null +++ b/doc/JalviewJS-API.md @@ -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 index 0000000..9ff3352 --- /dev/null +++ b/doc/JalviewJS-startupParams.md @@ -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 index 0000000..d32fc48 Binary files /dev/null and b/doc/JalviewJS-startupParams.xlsx differ diff --git a/doc/building.html b/doc/building.html index cc5eb30..2ca2641 100644 --- a/doc/building.html +++ b/doc/building.html @@ -1,724 +1,691 @@ + - - - - + + + + Building Jalview from Source - - - + + - -
+
-

Building Jalview from Source

-

tl;dr

+ + +

Building Jalview from Source

+

tl;dr

# 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
-

Setting up

+java -jar getdown-launcher.jar . jalview + +

Setting up

-

To get set up using only the Eclipse IDE (https://www.eclipse.org/) then please see the section Setting up in Eclipse IDE

+

To get set up using only the Eclipse IDE (https://www.eclipse.org/) then please see the section Setting up in Eclipse IDE

-

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.

+

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.

-

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).

+

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).

-

Java 11 compliant JDK

-

All platforms

-

We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=hotspot, either the Installer or .zip/.tar.gz variants whichever you prefer (if you're not sure, choose the Installer).

+

Java 11 compliant JDK

+

All platforms

+

We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=hotspot, either the Installer or .zip/.tar.gz variants whichever you prefer (if you're not sure, choose the Installer).

-
Alternative/CLI install of AdoptOpenJDK 11
-

You can also install adoptopenjdk11 using either brew (macOS), choco (Windows) (see the section on gradle and git for more informaiton on brew and choco) or yum or apt (Linux):

-
alternative for MacOS and Homebrew
+
Alternative/CLI install of AdoptOpenJDK 11
+

You can also install adoptopenjdk11 using either brew (macOS), choco (Windows) +(see the section on gradle and git for more informaiton on brew and choco) +or yum or apt (Linux):

+
alternative for MacOS and Homebrew
brew tap adoptopenjdk/openjdk
-brew cask install adoptopenjdk11
-
alternative for Windows and Chocolatey
-
choco install adoptopenjdk11
-
alternative for Linux with yum/apt
-

see https://adoptopenjdk.net/installation.html#linux-pkg

+brew cask install adoptopenjdk11 + +
alternative for Windows and Chocolatey
+
choco install adoptopenjdk11
+
+
alternative for Linux with yum/apt
+

see https://adoptopenjdk.net/installation.html#linux-pkg

-

gradle and git

+

gradle and git

You should be able to install the latest (or sufficiently recent) versions of gradle and git using your OS package manager.

-

MacOS

-

we recommend using brew, which can be installed following the instructions at https://brew.sh/. After installing brew, open a Terminal window and type in (using an Administrator privileged user):

-
brew install gradle git
+

MacOS

+

we recommend using brew, which can be installed following the instructions at https://brew.sh/. +After installing brew, open a Terminal window and type in (using an Administrator privileged user):

+
brew install gradle git
+

or if you aready have them installed but need to upgrade the version:

-
brew upgrade gradle git
-

Windows

-

we suggest using the Chocolatey package manager. See install instructions at https://chocolatey.org/, and you will just need

-
choco install gradle
-choco install git
-

Alternatively, you could install a real bash shell and install both gradle and git through apt-get. See https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/ for how to install the ubuntu bash shell in Windows 10.

-

Another alternative would be to install them separately. For gradle follow the instructions at https://gradle.org/install/, and for git here are a couple of suggestions: Git for Windows https://gitforwindows.org/. Getting the individual installs working together on the command line will be trickier so we recommend using Chocolatey or bash.

-

Linux

+
brew upgrade gradle git
+
+

Windows

+

we suggest using the Chocolatey package manager. See install instructions at https://chocolatey.org/, and you will just need

+
choco install gradle
+choco install git
+
+

Alternatively, you could install a real bash shell and install both gradle and git through apt-get. +See https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/ +for how to install the ubuntu bash shell in Windows 10.

+

Another alternative would be to install them separately. For gradle follow the instructions at https://gradle.org/install/, and for git here are a couple of suggestions: Git for Windows https://gitforwindows.org/. +Getting the individual installs working together on the command line will be trickier +so we recommend using Chocolatey or bash.

+

Linux

this will depend on which distribution you're using.

-
For Debian based distributions (e.g. Mint, Ubuntu, Debian)
+
For Debian based distributions (e.g. Mint, Ubuntu, Debian)

run

-
 sudo apt-get install gradle git
-
for RPM-based distributions (e.g. Fedora, CentOS, RedHat)
+
 sudo apt-get install gradle git
+
+
for RPM-based distributions (e.g. Fedora, CentOS, RedHat)

run

-
sudo yum install gradle git
+
sudo yum install gradle git
+

If you have some other version of linux you'll probably be able to work it out!

-

Downloading the Jalview source tree

-

This can be done with git. On the command line, change directory to where you want to download Jalview's build-tree top level directory. Then run

-
git clone http://source.jalview.org/git/jalview.git
-

You'll get some progress output and after a minute or two you should have the full Jalview build-tree in the folder jalview.

-

What's in the source tree?

-

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 jalview tree.

+

Downloading the Jalview source tree

+

This can be done with git. +On the command line, change directory to where you want to download Jalview's build-tree +top level directory. Then run

+
git clone http://source.jalview.org/git/jalview.git
+
+

You'll get some progress output and after a minute or two you should have the full +Jalview build-tree in the folder jalview.

+

What's in the source tree?

+

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 jalview tree.

Within the jalview folder you will find (of possible interest):

---- - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
dir/ or filecontains
dir/ or filecontains
bin/used by eclipse for compiled classes -- no need to touch this
build/the gradle build dir
classes/contains the compiled Java classes for the Jalview application
dist/assembled .jar files needed to run Jalview application
examples/example input files usable by Jalview
getdown/the libraries used by the Javliew launcher (getdown)
getdown/src/our modified source for getdown
getdown/website/the assembled "download" folder used by getdown for downloads/upgrades
getdown/files/the minimal fileset to launch the Jalview launcher, which can then download the rest of the Jalview application
help/the help documents
j8lib/libraries needed to run Jalview under Java 1.8
j11lib/libraries needed to run Jalivew under Java 11
resource/non-java resources used in the Jalview application
src/the Jalview application source .java files
test/Test class source files
utils/helper applications used in the build process
utils/install4j/files used by the packaging tool, install4j
build.gradlethe build file used by gradle
gradle.propertiesconfigurable properties for the build process
bin/used by eclipse for compiled classes -- no need to touch this
build/the gradle build dir
classes/contains the compiled Java classes for the Jalview application
dist/assembled .jar files needed to run Jalview application
examples/example input files usable by Jalview
getdown/the libraries used by the Javliew launcher (getdown)
getdown/src/our modified source for getdown
getdown/website/the assembled "download" folder used by getdown for downloads/upgrades
getdown/files/the minimal fileset to launch the Jalview launcher, which can then download the rest of the Jalview application
help/the help documents
j8lib/libraries needed to run Jalview under Java 1.8
j11lib/libraries needed to run Jalivew under Java 11
resource/non-java resources used in the Jalview application
src/the Jalview application source .java files
test/Test class source files
utils/helper applications used in the build process
utils/install4j/files used by the packaging tool, install4j
build.gradlethe build file used by gradle
gradle.propertiesconfigurable properties for the build process
RELEASEpropertyfile configuring JALVIEW_VERSION (from jalview.version) and the release branch (from jalview.release). An alternative file can be specified via JALVIEW_RELEASE_FILE property

Note that you need a Java 11 JDK to compile Jalview whether your target build is Java 1.8 or Java 11.

-

Building Jalview

-

You will need to have the Java 11 javac in your path, or alternatively you can configure gradle to know where this is by putting

-
org.gradle.java.home=/path_to_jdk_directory
+

Building Jalview

+

You will need to have the Java 11 javac in your path, or alternatively you can configure +gradle to know where this is by putting

+
org.gradle.java.home=/path_to_jdk_directory
+

in the gradle.properties file.

You may want to see some of the other properties you can change at the end of this document.

-

Minimal Jalview Build

+

Minimal Jalview Build

To compile the necessary class files, just run

-
gradle compileJava
-

to compile the classes into the classes folder. You should now be able to run the Jalview application directly with

-
java -cp "classes:resources:help:j11lib/*" jalview.bin.Jalview
-

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

-
java -cp "classes:resources:help:j11lib/*" jalview.bin.Launcher
+
gradle compileJava
+
+

to compile the classes into the classes folder. +You should now be able to run the Jalview application directly with

+
java -cp "classes:resources:help:j11lib/*" jalview.bin.Jalview
+
+

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

+
java -cp "classes:resources:help:j11lib/*" jalview.bin.Launcher
+
-

You must use just "j11lib/*" and not "j11lib/*.jar" as this is a special Java classpath argument wildcard interpreted by java, not a shell expansion wildcard interpreted by the shell.

+

You must use just "j11lib/*" and not "j11lib/*.jar" as this is a special Java +classpath argument wildcard interpreted by java, not a shell expansion wildcard interpreted +by the shell.

-

Note that jalview.bin.Launcher is a simplified launcher class that re-launches jalview.bin.Jalview with the same JRE (not the same JVM instance), classpath and arguments, but with an automatically determined -Xmx... memory setting if one hasn't been provided.

-

Jalview in a Jar File

+

Note that jalview.bin.Launcher is a simplified launcher class that re-launches jalview.bin.Jalview +with the same JRE (not the same JVM instance), classpath and arguments, but with an automatically determined -Xmx... +memory setting if one hasn't been provided.

+

Jalview in a Jar File

To package the classes, resources, and help into one jar, you can run

-
gradle jar
+
gradle jar
+

which assembles the Jalview classes and resources into dist/jalview.jar

To run this, use

-
java -cp "dist/jalview.jar:j11lib/*" jalview.bin.Jalview
-

Distributed Jar Files

-

To simplify this, all required .jar files can be assembled into the dist folder using

-
gradle makeDist
+
java -cp "dist/jalview.jar:j11lib/*" jalview.bin.Jalview
+
+

Distributed Jar Files

+

To simplify this, all required .jar files can be assembled into the dist folder +using

+
gradle makeDist
+

which puts all required jar files into dist so you can run with

-
java -cp "dist/*" jalview.bin.Jalview
-

Single shadow Jar File

-

The shadow jar file is a single .jar that contains all required classes and resources from jalview.jar and all of the supporting libraries in j11lib/*.jar merged into one .jar archive file. A default launching class (MAIN-CLASS: jalview.bin.Launcher) is specified in the .jar manifest file (META/MANIFEST.MF) so a start class doesn't need to be specified.

+
java -cp "dist/*" jalview.bin.Jalview
+
+

Single shadow Jar File

+

The shadow jar file is a single .jar that contains all required classes and resources from jalview.jar +and all of the supporting libraries in j11lib/*.jar merged into one .jar archive +file. A default launching class (MAIN-CLASS: jalview.bin.Launcher) is specified in the .jar +manifest file (META/MANIFEST.MF) so a start class doesn't need to be specified.

Build the shadow jar file in build/lib/jalview-all-11.jar with

-
gradle shadowJar
+
gradle shadowJar
+

and run it with

-
java -jar build/lib/jalview-all-11.jar
-

Because no arguments are required, most OSes will associate a .jar file with the java application (if this has been installed through the OS and not just a local unzip) as a -jar argument so you may find you can launch jalview-all-11.jar just by double-clicking on it)!

+
java -jar build/lib/jalview-all-11.jar
+
+

Because no arguments are required, most OSes will associate a .jar file with the +java application (if this has been installed through the OS and not just a local +unzip) as a -jar argument so you may find you can launch jalview-all-11.jar +just by double-clicking on it)!

-

The shadowJar task is not a requirement for any other task, so to build the shadow jar file you must specify the shadowJar task.

+

The shadowJar task is not a requirement for any other task, so to build the shadow +jar file you must specify the shadowJar task.

-

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 getdown launcher.

+

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 getdown launcher.

-

Building the getdown launcher

-

We have made significant customisations to the getdown launcher which you can find in getdown/src/getdown.

+

Building the getdown launcher

+

We have made significant customisations to the getdown launcher which you can find +in getdown/src/getdown.

-

You don't need to build this afresh as the required gradle-core.jar and gradle-launcher.jar files are already distributed in j11lib and getdown/lib but if you want to, then you'll need a working Maven and also a Java 8 JDK. Ensure the Java 8 javac is forefront in your path and do

-
cd getdown/src/getdown
-mvn clean package -Dgetdown.host.whitelist="jalview.org,*.jalview.org"
-

and you will find the required .jar files in core/target/gradle-core-XXX.jar and launcher/target/gradle-launcher-XXX.jar. The gradle-core.jar should then be copied to all three of the j8lib, j11lib and getdown/lib folders, whilst the gradle-launcher.jar only needs to be copied to getdown/lib.

-

The mvn command should ideally include the -Dgetdown.host.whitelist=*.jalview.org setting. This, and the necessary file copying commands, can be found in getdown/src/getdown/mvn_cmd.

+

You don't need to build this afresh as the required gradle-core.jar +and gradle-launcher.jar files are already distributed in j11lib and getdown/lib but if you want to, then +you'll need a working Maven and also a Java 8 JDK. Ensure the Java 8 javac is forefront +in your path and do

+
cd getdown/src/getdown
+mvn clean package -Dgetdown.host.whitelist="jalview.org,*.jalview.org"
+
+

and you will find the required .jar files in core/target/gradle-core-XXX.jar +and launcher/target/gradle-launcher-XXX.jar. The gradle-core.jar should then be copied +to all three of the j8lib, j11lib and getdown/lib folders, whilst the gradle-launcher.jar only +needs to be copied to getdown/lib.

+

The mvn command should ideally include the -Dgetdown.host.whitelist=*.jalview.org setting. +This, and the necessary file copying commands, can be found in getdown/src/getdown/mvn_cmd.

To assemble Jalview with getdown use the following gradle task:

-
gradle getdown
-

This puts all the necessary files to launch Jalview with getdown into getdown/website/11/. This could be treated as the reference folder for getdown, 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.

-

A minimal getdown-launcher can be found in getdown/files/11/ which checks its up-to-date status with (the absolute path to) getdown/website/11/.

+
gradle getdown
+
+

This puts all the necessary files to launch Jalview with getdown +into getdown/website/11/. This could be treated as the reference folder +for getdown, 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.

+

A minimal getdown-launcher can be found in getdown/files/11/ which checks its up-to-date +status with (the absolute path to) getdown/website/11/.

This can be launched with

-
java -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview
+
java -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview
+
-

We've already met the -jar file.jar arguments. The next argument is the working folder for getdown, and the final argument, "jalview", is a getdown application id (only "jalview" is defined here).

+

We've already met the -jar file.jar arguments. The next argument is the working folder for +getdown, and the final argument, "jalview", is a getdown application id (only "jalview" +is defined here).

-

Running tests

+

Running tests

There are substantial tests written for Jalview that use TestNG, which you can run with

-
gradle test
-

These normally take around 5 - 10 minutes to complete and outputs its full results into the tests/ folder. A summary of results should appear in your console.

+
gradle test
+
+

These normally take around 5 - 10 minutes to complete and outputs its full results into +the tests/ folder. A summary of results should appear in your console.

You can run different defined groups of tests with

-
gradle test -PtestngGroups=Network
+
gradle test -PtestngGroups=Network
+

Available groups include Functional (default), Network, External.

-

Excluding some tests

+

Excluding some tests

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.

To exclude one or more groups of tests, add them as a comma separated list in testngExcludedGroups.

-
gradle test -PtestngExcludedGroups=Not-bamboo
-

Installer packaging with install4j

-

Jalview is currently using install4j https://www.ej-technologies.com/products/install4j/overview.html as its installer packaging tool.

-

If you have a licensed installation of install4j you can build Jalview installers by running

-
gradle installers
-

though you may need to fiddle with the install4j and copyInstall4jTemplate tasks in build.gradle file to point to your installation of install4j and also to bundled JREs if you want to bundle those into the installers.

-

If you want more details, get in touch on our development mailing list jalview-dev@jalview.org. Sign up at http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev.

-

Gradle properties

-

There are a lot of properties configured in gradle.properties which we strongly recommend being left as they are unless you have a specific problem with the build process.

-

There are a few gradle properties you might want to set on the command line with the -P flag when building a version of Jalview with specific requirements:

-

JAVA_VERSION

-

This changes the target java bytecode version > NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build Jalview for any byte-code target version.

+
gradle test -PtestngExcludedGroups=Not-bamboo
+
+

Installer packaging with install4j

+

Jalview is currently using install4j https://www.ej-technologies.com/products/install4j/overview.html +as its installer packaging tool.

+

If you have a licensed installation of install4j you can build Jalview installers +by running

+
gradle installers
+
+

though you may need to fiddle with the install4j and copyInstall4jTemplate tasks +in build.gradle file to point to your installation of install4j and also to bundled +JREs if you want to bundle those into the installers.

+

If you want more details, get in touch on our development mailing list jalview-dev@jalview.org. +Sign up at http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev.

+

Gradle properties

+

There are a lot of properties configured in gradle.properties which we strongly recommend +being left as they are unless you have a specific problem with the build process.

+

There are a few gradle properties you might want to set on the command line with the +-P flag when building a version of Jalview with specific requirements:

+

JAVA_VERSION

+

This changes the target java bytecode version

+
+

NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build +Jalview for any byte-code target version.

+

Valid values are 11 and 1.8.

e.g.

-
gradle shadowJar -PJAVA_VERSION=1.8
+
gradle shadowJar -PJAVA_VERSION=1.8
+

When using -PJAVA_VERSION=1.8 the libraries from j8lib (instead of j11lib) will be used in the compile
-and runtime classpath and also used in the makeDist build step. Where a Java version of 11 is used in folder and file names, it will instead use 1.8. Also if you are building installer packages with install4j the package builder will look for JRE 1.8 bundles to package in the installers.

+and runtime classpath and also used in the makeDist build step. Where a Java version of 11 is used in folder and file names, it will +instead use 1.8. Also if you are building installer packages with install4j the +package builder will look for JRE 1.8 bundles to package in the installers.

-

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.

+

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.

-

CHANNEL

-

This changes the appbase setting in getdown.txt (appbase 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 appbase to be a local directory on the filesystem (instead of a website URL) then a modified version of the getdown-launcher.jar will be used to allow this. The two versions of the getdown-launcher.jar can be found in getdown/lib. Some other variables used in the build process might also be set differently depending on the value of CHANNEL to allow smooth operation of getdown in the given context.

-

There are several values of CHANNEL that can be chosen, with a default of LOCAL. Here's what they're for and what they do:

+

CHANNEL

+

This changes the appbase setting in getdown.txt (appbase 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 appbase to be a local +directory on the filesystem (instead of a website URL) then a modified version of the getdown-launcher.jar will +be used to allow this. The two versions of the getdown-launcher.jar can be found in getdown/lib. +Some other variables used in the build process might also be set differently depending on the value of CHANNEL +to allow smooth operation of getdown in the given context.

+

There are several values of CHANNEL that can be chosen, with a default of LOCAL. Here's what they're for and what they do:

+ +
  • BUILD: This is for creating an appbase channel on the build server by an automatic or manually started build. +It will set
  • -
  • DEVELOP: This is for creating a develop 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 + +
  • +
  • DEVELOP: This is for creating a develop 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
  • -
  • SCRATCH-NAME: 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 release or develop channels. The value of NAME can be any "word-character" [A-Za-z0-9_] It will set + +
  • +
  • SCRATCH-NAME: 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 release or develop channels. The value of NAME can be any "word-character" [A-Za-z0-9_] +It will set
  • -
  • TEST-LOCAL: Like SCRATCH but with a specific test-local channel name and a local filesystem appbase. This is meant for testing an over-the-air update on the local filesystem. An extra property LOCALDIR must be given (e.g. -PLOCALDIR=/home/user/tmp/test) It will set + +
  • +
  • TEST-LOCAL: Like SCRATCH but with a specific test-local channel name and a local filesystem appbase. This is meant for testing an over-the-air update on the local filesystem. An extra property LOCALDIR must be given (e.g. -PLOCALDIR=/home/user/tmp/test) +It will set
  • -
  • TEST-RELEASE: Like SCRATCH but with a specific test-release 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 release or develop channels. It will set + +
  • +
  • TEST-RELEASE: Like SCRATCH but with a specific test-release 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 release or develop channels. +It will set
  • -
  • RELEASE: This is for an actual release build, and will use an appbase on the main web server with the main release channel name. This won't become live until the actual getdown artefact is synced to the web server. It will set + +
  • +
  • RELEASE: This is for an actual release build, and will use an appbase on the main web server with the main release channel name. This won't become live until the actual getdown artefact is synced to the web server. +It will set
  • -
  • ARCHIVE: 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 archive/JALVIEW_VERSION channel name. This won't become live until the actual getdown artefact is synced to the web server. You must also specify an ARCHIVEDIR property that points to an earlier version of Jalview with a dist directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set + +
  • +
  • ARCHIVE: 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 archive/JALVIEW_VERSION channel name. This won't become live until the actual getdown artefact is synced to the web server. +You must also specify an ARCHIVEDIR property that points to an earlier version of Jalview with a dist directory containing the required jar files. This should create a getdown structure and digest with the older jar files. +It will set
  • -
  • ARCHIVELOCAL: Like ARCHIVE but with a local filesystem appbase for local testing. You must also specify an ARCHIVEDIR property that points to an earlier version of Jalview with a dist directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set + +
  • +
  • ARCHIVELOCAL: Like ARCHIVE but with a local filesystem appbase for local testing. +You must also specify an ARCHIVEDIR property that points to an earlier version of Jalview with a dist directory containing the required jar files. This should create a getdown structure and digest with the older jar files. +It will set
  • + +

    e.g.

    -
    gradle getdown -PCHANNEL=SCRATCH-my_test_version
    -

    install4jMediaTypes

    -

    If you are building install4j installers (requires install4j to be installed) then this property specifies a comma-separated list of media types (i.e. platform specific installers) install4j should actually build.

    -

    Currently the valid values are linuxDeb, linuxRPM, macosArchive, unixArchive, unixInstaller, windows

    +
    gradle getdown -PCHANNEL=SCRATCH-my_test_version
    +
    +

    JALVIEW_VERSION and the RELEASE file

    +

    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 jalview.version property specified in the RELEASE file:

    + +

    It is also possible to specify a custom location for the RELEASE file via an optional JALVIEW_RELEASE_FILE property.

    +

    install4jMediaTypes

    +

    If you are building install4j installers (requires install4j to be installed) then this property specifies a comma-separated +list of media types (i.e. platform specific installers) install4j should actually build.

    +

    Currently the valid values are +linuxDeb, +linuxRPM, +macosArchive, +unixArchive, +unixInstaller, +windows

    The default value is all of them.

    e.g.

    -
    gradle installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive
    +
    gradle installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive
    +

    To get an up-to-date list of possible values, you can run

    -
    perl -n -e 'm/^\s*<(\w+)[^>]*\bmediaFileName=/ && print "$1\n";' utils/install4j/install4j_template.install4j  | sort -u
    +
    perl -n -e 'm/^\s*<(\w+)[^>]*\bmediaFileName=/ && print "$1\n";' utils/install4j/install4j_template.install4j  | sort -u
    +

    in the jalview root folder.

    -

    Enabling Code Coverage with OpenClover

    +

    Enabling Code Coverage with OpenClover

    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.

    gradle -Pclover=true test cloverReport

    -

    Troubleshooting report generation

    +

    Troubleshooting report generation

    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:

    -
    JVM Memory settings - increase if out of memory errors are reported
    +
    JVM Memory settings - increase if out of memory errors are reported

    cloverReportJVMHeap = 2g

    -
    -Dfile.encoding=UTF-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.

    cloverReportJVMArgs = -Dfile.encoding=UTF-8

    -
    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

    cloverReportHTMLOptions =

    -
    -v for verbose, -d for debug level messages (as above)
    +
    -v for verbose, -d for debug level messages (as above)

    cloverReportXMLOptions =

    Note 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.

    -

    Setting up in Eclipse IDE

    -

    Installing Eclipse IDE

    -

    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.

    -

    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: https://www.eclipse.org/downloads/. 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 "Eclipse IDE for Enterprise Java Developers" package.

    -
    - - -
    +

    Setting up in Eclipse IDE

    +

    Installing Eclipse IDE

    +

    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.

    +

    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: https://www.eclipse.org/downloads/. 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 "Eclipse IDE for Enterprise Java Developers" package.

    +

    Once Eclipse is installed, we also recommend installing several plugins from the Eclipse Marketplace.

    Some of these should already be installed with the Enterprise Java Developer package:

    -
      +
      1. Buildship Gradle Integration 3.0 (or greater)
      2. EclEmma Java Code Coverage
      3. Egit - Git Integration for Eclipse

      To install the others, launch Eclipse, and go to Help->Eclipse Marketplace...

      Search for and install:

      -
        +
        1. Groovy Development Tools 3.4.0 (or greater)
        2. Checkstyle Plug-in (optional)
        3. TestNG for Eclipse (optional -- only needed if you want to run tests from Eclipse)
        -

        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

        +

        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

        Help->Install New Software...

        and entering

        TestNG Release - https://dl.bintray.com/testng-team/testng-eclipse-release

        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 a selection box under the Name column.

        -

        Select TestNG and carry on through the install process to install the TestNG plugin.

        +

        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 +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).

        -

        To do this go to Preferences (Eclipse->Preferences in macOS, File->Preferences on Windows or Window->Preferences on Linux) and find

        +

        To do this go to Preferences (Eclipse->Preferences in macOS, File->Preferences +on Windows or Window->Preferences on Linux) and find

        Java -> Installed JREs

        If your Java 11 installation is not listed, click on

        Add -> Standard VM -> Next

        -

        and enter the JRE home. You can browse to where it is installed. Give it a name (like "AdoptOpenJDK 11"). Select this JDK as the default JRE and click on Apply and Close.

        +

        and enter the JRE home. You can browse to where it is installed. Give it a name (like "AdoptOpenJDK 11"). Select this JDK +as the default JRE and click on Apply and Close.

        You can now import Jalview.

        -

        Importing Jalview as an Eclipse project

        -

        Importing an already downloaded git repo

        +

        Importing Jalview as an Eclipse project

        +

        Importing an already downloaded git repo

        If you have already downloaded Jalview using git clone then you can import this folder into Eclipse directly.

        -

        It is important to import Jalview as a Gradle project (not as a Java project), so go to

        +

        It is important to import +Jalview as a Gradle project (not as a Java project), so go to

        File->Import...

        find and select

        Gradle->Existing Gradle Project

        and then click on the Next button.

        -

        In the following options, it is the Project Root Directory you should set to be the jalview folder that git downloaded. Then you can click on the Finish button.

        -

        Using Eclipse IDE to download the git repo

        -

        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

        +

        In the following options, it is the Project Root Directory you should set to be the +jalview folder that git downloaded. Then you can click on the Finish button.

        +

        Using Eclipse IDE to download the git repo

        +

        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

        File->Import...

        Find and select

        Git->Projects from Git

        and then click on the Next button.

        Then select Clone URI and click on Next.

        -

        In the next window (Source Git Repository) you should put the git clone URL in the text box labelled URI. If you have a Jalview developer account (with a username and password for the Jalview git repository) then you should enter https://source.jalview.org/git/jalview.git. If you do not have a Jalview developer account then you should enter http://source.jalview.org/git/jalview.git. 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. > You can sign up for a Jalview developer account at https://source.jalview.org/crucible/

        -

        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.

        -
        -Eclipse eGit connection configuration -

        Eclipse eGit connection configuration

        -
        +

        In the next window (Source Git Repository) you should put the git clone URL in the text box labelled URI. If you have a Jalview developer account (with a username and password for the Jalview git repository) then you should enter +https://source.jalview.org/git/jalview.git. +If you do not have a Jalview developer account then you should enter +http://source.jalview.org/git/jalview.git. +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.

        +
        +

        You can sign up for a Jalview developer account at https://source.jalview.org/crucible/

        +
        +

        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.

        +

        Eclipse eGit connection configuration

        Click on the Next button.

        -

        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 develop branch so you can click on the Deselect All button, find the develop branch (the filter text helps), select that, and then click on the Next button.

        -

        Choose a directory to your copy of the git repo in, and leave the other options as they are and click on the Next button. The next stage may take a minute or two as it checks out the selected branch(es) from the Jalview git repository.

        -

        When it has finished it is important to select Import as general project and then click on Next. > Ideally there would be an Import as gradle project here but there isn't -- we'll sort that out later.

        -
        -Eclipse eGit import choice -

        Eclipse eGit import choice

        -
        +

        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 develop branch so you can click on the Deselect All button, find the develop branch (the filter text helps), select that, and then click on the Next button.

        +

        Choose a directory to your copy of the git repo in, and leave the other options as they are and click on the Next button. The next stage may take a minute or two as it checks out the selected branch(es) from the Jalview git repository.

        +

        When it has finished it is important to select Import as general project and then click on Next.

        +
        +

        Ideally there would be an Import as gradle project here but there isn't -- we'll sort that out later.

        +
        +

        Eclipse eGit import choice

        Click on the Next button.

        -

        You can change the project name here. By default it will show as jalview 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.

        +

        You can change the project name here. By default it will show as jalview 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.

        Click on Finish!

        However, we haven't finished...

        -

        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!

        +

        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!

        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.

        -
        -Eclipse Add Gradle Nature -

        Eclipse Add Gradle Nature

        -
        +

        Eclipse Add Gradle Nature

        The project should now reconfigure itself using the build.gradle file to dynamically set various aspects of the project including classpath.

        -

        Additional views

        -

        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->Show View->Console and Window->Show View->Other... Filter with the word "gradle" and choose both Gradle Executions and Gradle Tasks and then click on the Open button.

        -

        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 https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing

        +

        Additional views

        +

        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->Show View->Console +and +Window->Show View->Other... +Filter with the word "gradle" and choose both Gradle Executions and Gradle Tasks and then click on the Open button.

        +

        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 https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing


        Jalview Development Team

        - + + diff --git a/doc/building.md b/doc/building.md index ad19979..06e796d 100644 --- a/doc/building.md +++ b/doc/building.md @@ -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 index 0000000..7205420 --- /dev/null +++ b/doc/releaseprocess.html @@ -0,0 +1,692 @@ + + + + + + + + Making a Jalview release + + + + + +

        Making a Jalview release

        +

        Objectives

        +

        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

        +

        tl;dr

        +

        OSX_SIGNING=yes KEYPASS=MYSECRETKEY gradle -POSX_KEYPASS=true -POSX_KEYSTORE=/Users/jbb/buildtools/private/sourceofpain.p12 -PJSIGN_SH="/Users/jbb/buildtools/jsign.sh" -Pinstall4j_verbose=true -PJAVA_VERSION=1.8 -PCHANNEL=RELEASE -PVERSION=W.X.Y.Z getdown installers shadowJar +codesign -s "MY Apple Dev Name" build/install4j/1.8/Jalview-DEVELOPMENT-macos-java_8.dmg

        + + + diff --git a/gradle.properties b/gradle.properties index 745e8c7..b99cdca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -110,6 +110,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 @@ -128,9 +130,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 diff --git a/j11lib/Jmol-14.29.17-no_netscape.jar b/j11lib/Jmol-14.29.17-no_netscape.jar deleted file mode 100644 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 index 0000000..1672e67 Binary files /dev/null and b/j11lib/Jmol-15.1.3.jar differ diff --git a/j8lib/Jmol-14.29.17.jar b/j8lib/Jmol-14.29.17.jar deleted file mode 100644 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 index 0000000..1672e67 Binary files /dev/null and b/j8lib/Jmol-15.1.3.jar differ diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java b/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java index 22c9098..4378457 100644 --- a/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java +++ b/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java @@ -34,6 +34,7 @@ package ext.edu.ucsf.rbvi.strucviz2; import jalview.bin.Cache; import jalview.gui.Preferences; +import jalview.util.Platform; import java.io.File; import java.io.IOException; @@ -931,7 +932,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")) { diff --git a/src/jalview/analysis/AlignmentSorter.java b/src/jalview/analysis/AlignmentSorter.java index b88ef3a..af7db0a 100755 --- a/src/jalview/analysis/AlignmentSorter.java +++ b/src/jalview/analysis/AlignmentSorter.java @@ -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; /** * Sorts sequences in the alignment by Percentage Identity with the given @@ -115,93 +142,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 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 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 algn = align.getSequences(); - synchronized (algn) - { - List 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 @@ -221,17 +165,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); } /** @@ -254,17 +190,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); } /** @@ -281,14 +209,16 @@ public class AlignmentSorter // ORDERS BY GROUP SIZE List 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 @@ -315,7 +245,7 @@ public class AlignmentSorter // NOW ADD SEQUENCES MAINTAINING ALIGNMENT ORDER // ///////////////////////////////////////////// - List seqs = new ArrayList<>(); + List tmp = new ArrayList<>(); for (int i = 0; i < groups.size(); i++) { @@ -324,68 +254,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 tmp, - List mask) - { - // or? - // tmp2 = tmp.retainAll(mask); - // return tmp2.addAll(mask.removeAll(tmp2)) - - ArrayList 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); } /** @@ -401,24 +273,17 @@ public class AlignmentSorter // Get an ordered vector of sequences which may also be present in align List 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); } /** @@ -473,26 +338,19 @@ public class AlignmentSorter { List 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); } /** @@ -658,9 +516,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 @@ -670,18 +531,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. *

        @@ -708,14 +557,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(); @@ -734,8 +584,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 sfs = seqs[i].findFeatures(startCol + 1, endCol + 1, types); @@ -809,6 +659,8 @@ public class AlignmentSorter } } + boolean doSort = false; + if (FEATURE_SCORE.equals(method)) { if (hasScores == 0) @@ -834,7 +686,7 @@ public class AlignmentSorter } } } - QuickSort.sortByDouble(scores, seqs, sortByFeatureAscending); + doSort = true; } else if (FEATURE_DENSITY.equals(method)) { @@ -846,9 +698,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); } @@ -883,16 +739,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 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 seqList = align.getSequences(); + synchronized (seqList) + { + List 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 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 seqList, + List tmp) + { + ArrayList 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()]); } } diff --git a/src/jalview/analysis/CrossRef.java b/src/jalview/analysis/CrossRef.java index c54357e..b70e9f7 100644 --- a/src/jalview/analysis/CrossRef.java +++ b/src/jalview/analysis/CrossRef.java @@ -31,6 +31,7 @@ import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.util.DBRefUtils; import jalview.util.MapList; +import jalview.ws.SequenceFetcher; import jalview.ws.SequenceFetcherFactory; import jalview.ws.seqfetcher.ASequenceFetcher; @@ -402,7 +403,6 @@ public class CrossRef private void retrieveCrossRef(List sourceRefs, SequenceI seq, List xrfs, boolean fromDna, AlignedCodonFrame cf) { - ASequenceFetcher sftch = SequenceFetcherFactory.getSequenceFetcher(); SequenceI[] retrieved = null; SequenceI dss = seq.getDatasetSequence() == null ? seq : seq.getDatasetSequence(); @@ -418,7 +418,7 @@ public class CrossRef } try { - retrieved = sftch.getSequences(sourceRefs, !fromDna); + retrieved = SequenceFetcher.getInstance().getSequences(sourceRefs, !fromDna); } catch (Exception e) { System.err.println( diff --git a/src/jalview/analysis/scoremodels/ScoreModels.java b/src/jalview/analysis/scoremodels/ScoreModels.java index ebc9a26..8700ec0 100644 --- a/src/jalview/analysis/scoremodels/ScoreModels.java +++ b/src/jalview/analysis/scoremodels/ScoreModels.java @@ -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 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 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/JalviewApp.java b/src/jalview/api/JalviewApp.java deleted file mode 100644 index e488d6e..0000000 --- a/src/jalview/api/JalviewApp.java +++ /dev/null @@ -1,82 +0,0 @@ -package jalview.api; - -import jalview.datamodel.ColumnSelection; -import jalview.datamodel.HiddenColumns; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceGroup; -import jalview.datamodel.SequenceI; -import jalview.io.DataSourceType; -import jalview.io.NewickFile; -import jalview.javascript.JSFunctionExec; -import jalview.javascript.MouseOverStructureListener; -import jalview.structure.SelectionSource; -import jalview.structure.VamsasSource; - -//import java.applet.AppletContext; -import java.io.IOException; -import java.net.URL; -import java.util.Hashtable; -import java.util.Vector; - -import netscape.javascript.JSObject; - -public interface JalviewApp -{ - public String getParameter(String name); - - public boolean getDefaultParameter(String name, boolean def); - - public URL getDocumentBase(); - - public URL getCodeBase(); - - public void setAlignPdbStructures(boolean defaultParameter); - - public void newStructureView(PDBEntry pdb, SequenceI[] seqs, - String[] chains, DataSourceType protocol); - - public void alignedStructureView(PDBEntry[] pdb, SequenceI[][] seqs, - String[][] chains, String[] protocols); - - public void updateForAnnotations(); - - public AlignViewportI getViewport(); - - public void setFeatureGroupState(String[] groups, boolean state); - - public boolean parseFeaturesFile(String param, DataSourceType protocol); - - public void newFeatureSettings(); - - public boolean loadScoreFile(String sScoreFile) throws IOException; - - public void loadTree(NewickFile fin, String treeFile) throws IOException; - - public Vector getJsExecQueue(JSFunctionExec jsFunctionExec); - -// deprecated public AppletContext getAppletContext(); - - public boolean isJsfallbackEnabled(); - - public JSObject getJSObject(); - - public StructureSelectionManagerProvider getStructureSelectionManagerProvider(); - - public void updateColoursFromMouseOver(Object source, - MouseOverStructureListener mouseOverStructureListener); - - public Object[] getSelectionForListener(SequenceGroup seqsel, ColumnSelection colsel, - HiddenColumns hidden, SelectionSource source, Object alignFrame); - - public String arrayToSeparatorList(String[] array); - - public Hashtable getJSHashes(); - - Hashtable> getJSMessages(); - - public Object getFrameForSource(VamsasSource source); - - public jalview.renderer.seqfeatures.FeatureRenderer getNewFeatureRenderer( - AlignViewportI vp); - -} diff --git a/src/jalview/api/JalviewJSApi.java b/src/jalview/api/JalviewJSApi.java new file mode 100644 index 0000000..895fd15 --- /dev/null +++ b/src/jalview/api/JalviewJSApi.java @@ -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); + +} diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index e1bfbde..311f1e7 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -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(); } } diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 1a46585..fc1f26d 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -882,7 +882,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } else if (source == autoCalculate) { - viewport.autoCalculateConsensus = autoCalculate.getState(); + viewport.setAutoCalculateConsensusAndConservation(autoCalculate.getState()); } else if (source == sortByTree) { @@ -1701,8 +1701,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, // viewport.getColumnSelection().getHiddenColumns() // != null; updateEditMenuBar(); - originalSource.firePropertyChange("alignment", null, - originalSource.getAlignment().getSequences()); + originalSource.notifyAlignment(); } /** @@ -1734,8 +1733,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, // != null; updateEditMenuBar(); - originalSource.firePropertyChange("alignment", null, - originalSource.getAlignment().getSequences()); + originalSource.notifyAlignment(); } AlignmentViewport getOriginatingSource(CommandI command) @@ -2084,8 +2082,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(); } @@ -2159,8 +2156,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) { @@ -2372,7 +2368,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } } - viewport.firePropertyChange("alignment", null, al.getSequences()); + viewport.notifyAlignment(); } } @@ -2416,7 +2412,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(); } @@ -2450,7 +2446,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, ranges.setStartRes(seq.findIndex(startRes) - 1); - viewport.firePropertyChange("alignment", null, al.getSequences()); + viewport.notifyAlignment(); } diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index 0324d8c..b379c2d 100644 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -352,7 +352,7 @@ public class AlignViewport extends AlignmentViewport if (mappedCommand != null) { mappedCommand.doCommand(null); - firePropertyChange("alignment", null, getAlignment().getSequences()); + notifyAlignment(); // ap.scalePanelHolder.repaint(); // ap.repaint(); diff --git a/src/jalview/appletgui/RedundancyPanel.java b/src/jalview/appletgui/RedundancyPanel.java index bd36b0d..6488c61 100644 --- a/src/jalview/appletgui/RedundancyPanel.java +++ b/src/jalview/appletgui/RedundancyPanel.java @@ -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); diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index 776e9ad..a1e2340 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -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; diff --git a/src/jalview/appletgui/TreeCanvas.java b/src/jalview/appletgui/TreeCanvas.java index cb26fb5..46d6c11 100755 --- a/src/jalview/appletgui/TreeCanvas.java +++ b/src/jalview/appletgui/TreeCanvas.java @@ -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()); } } diff --git a/src/jalview/javascript/JSFunctionExec.java b/src/jalview/appletgui/js/JSFunctionExec.java similarity index 99% rename from src/jalview/javascript/JSFunctionExec.java rename to src/jalview/appletgui/js/JSFunctionExec.java index 29f3fa9..247552b 100644 --- a/src/jalview/javascript/JSFunctionExec.java +++ b/src/jalview/appletgui/js/JSFunctionExec.java @@ -18,7 +18,7 @@ * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ -package jalview.javascript; +package jalview.appletgui.js; import jalview.bin.JalviewLite; diff --git a/src/jalview/javascript/JalviewLiteJsApi.java b/src/jalview/appletgui/js/JalviewLiteJsApi.java similarity index 99% rename from src/jalview/javascript/JalviewLiteJsApi.java rename to src/jalview/appletgui/js/JalviewLiteJsApi.java index b5811aa..f62beeb 100644 --- a/src/jalview/javascript/JalviewLiteJsApi.java +++ b/src/jalview/appletgui/js/JalviewLiteJsApi.java @@ -18,7 +18,7 @@ * along with Jalview. If not, see . * 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); diff --git a/src/jalview/javascript/JsCallBack.java b/src/jalview/appletgui/js/JsCallBack.java similarity index 97% rename from src/jalview/javascript/JsCallBack.java rename to src/jalview/appletgui/js/JsCallBack.java index 76d6e8d..2fe4dac 100644 --- a/src/jalview/javascript/JsCallBack.java +++ b/src/jalview/appletgui/js/JsCallBack.java @@ -18,7 +18,7 @@ * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ -package jalview.javascript; +package jalview.appletgui.js; public interface JsCallBack { diff --git a/src/jalview/javascript/JsSelectionSender.java b/src/jalview/appletgui/js/JsSelectionSender.java similarity index 99% rename from src/jalview/javascript/JsSelectionSender.java rename to src/jalview/appletgui/js/JsSelectionSender.java index c2a963e..b774813 100644 --- a/src/jalview/javascript/JsSelectionSender.java +++ b/src/jalview/appletgui/js/JsSelectionSender.java @@ -18,7 +18,7 @@ * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ -package jalview.javascript; +package jalview.appletgui.js; import jalview.appletgui.AlignFrame; import jalview.bin.JalviewLite; diff --git a/src/jalview/javascript/MouseOverListener.java b/src/jalview/appletgui/js/MouseOverListener.java similarity index 99% rename from src/jalview/javascript/MouseOverListener.java rename to src/jalview/appletgui/js/MouseOverListener.java index 6a4d0f8..59c13eb 100644 --- a/src/jalview/javascript/MouseOverListener.java +++ b/src/jalview/appletgui/js/MouseOverListener.java @@ -18,7 +18,7 @@ * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ -package jalview.javascript; +package jalview.appletgui.js; import jalview.appletgui.AlignFrame; import jalview.bin.JalviewLite; diff --git a/src/jalview/javascript/MouseOverStructureListener.java b/src/jalview/appletgui/js/MouseOverStructureListener.java similarity index 99% rename from src/jalview/javascript/MouseOverStructureListener.java rename to src/jalview/appletgui/js/MouseOverStructureListener.java index 6071933..7a16009 100644 --- a/src/jalview/javascript/MouseOverStructureListener.java +++ b/src/jalview/appletgui/js/MouseOverStructureListener.java @@ -18,12 +18,13 @@ * along with Jalview. If not, see . * 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 index 6a23c39..726734d 100644 --- a/src/jalview/bin/AppletParams.java +++ b/src/jalview/bin/AppletParams.java @@ -1,23 +1,33 @@ -package jalview.bin; -import jalview.gui.Preferences; +package jalview.bin; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Vector; + +import jalview.gui.Preferences; /** - * Collection of all known applet tags from JalviewLite + * 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 +public class AppletParams extends HashMap { - private final static String[] params = { "alignpdbfiles", - Preferences.ANNOTATIONCOLOUR_MAX, Preferences.ANNOTATIONCOLOUR_MIN, + private final static String[] params = { + "alignpdbfiles", + "ANNOTATIONCOLOUR_MAX", "ANNOTATIONCOLOUR_MIN", "annotations", "APPLICATION_URL", "automaticScrolling", "centrecolumnlabels", "debug", "defaultColour", "defaultColourNuc", "defaultColourProt", @@ -37,18 +47,10 @@ public class AppletParams extends HashMap "userDefinedColour", "widthScale", "windowHeight", "windowWidth", "wrap", }; - public AppletParams(Map info) - { - for (int i = params.length; --i >= 0;) - { - put(params[i], info.get(params[i])); - } - } - public String getParam(String param, String def) { - String val = get(param); - return (val != null ? val : def); + Object val = get(param); + return (val != null ? val.toString() : def); } // public AppletParams() { - // TODO Auto-generated constructor stub + } + + public static AppletParams getAppletParams(Map map, + List 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 map, String key) + { + Object o = map.get(key); + return (o == null ? null : o.toString()); } public static AppletParams getAppletParams(String[] args, - Vector vargs) + List vargs) { AppletParams appletParams = new AppletParams(); - String resourcePath = null; + String resourcePath = ""; for (int i = args.length; --i > 0;) // > 0 is correct, not >=0 { if (args[i].startsWith("name=\"Info.resourcePath\"")) @@ -129,265 +157,269 @@ public class AppletParams extends HashMap if (arg.startsWith("name=")) { String prefName = getAttr(arg, "name"); - String appletName = prefName.toLowerCase(); - String argName = prefName; String value = getAttr(arg, "value"); + addParam(vargs, prefName, value, appletParams, resourcePath); + } + } + return appletParams; + } - // note that Application arguments ARE case-sensitive, but - // Applet.getParameter() is not. + private static void addParam(List vargs, String prefName, + Object value, AppletParams appletParams, String resourcePath) + { - switch (appletName) - { + // note that Application arguments ARE case-sensitive, but + // Applet.getParameter() is not. - case "file": - argName = "open"; - appletName = null; - value = resourcePath + value; - break; - case "file2": - argName = "open2"; - prefName = null; - value = resourcePath + value; - break; - case "features": - case "jnetfile": - case "jpredfile": - case "pdbfile": - case "scorefile": - case "sequence": - // setting argName to null indicates that we want - // JalviewAppLoader to take care of this. - prefName = argName = null; - value = resourcePath + value; - 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"; - appletName = 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; - - // implemented; not tested: - - case "oninit": - prefName = null; - break; - case "annotations": - value = resourcePath + value; - argName = null; - break; - case "hidefeaturegroups": - // TODO - break; - case "pdbseq": - argName = prefName = null; - break; - case "sortbytree": - prefName = Preferences.SORT_BY_TREE; - value = checkTF(value); - appletName = null; // taken care of by Jalview - break; - case "format": - break; - case "alignpdbfiles": - argName = prefName = null; - break; - case "separator": - 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": - argName = prefName = null; - break; - - // TRUE/FALSE - - case "debug": - value = checkTF(value); - break; - case "embedded": - value = checkTF(value); - break; - 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 "showfeaturegroups": - value = checkTF(value); - break; - case "showfeaturesettings": - value = checkTF(value); - break; - case "showfullid": - value = checkTF(value); - break; - case "showtreebootstraps": - value = checkTF(value); - break; - case "showtreedistances": - value = checkTF(value); - break; - case "showunconserved": - prefName = Preferences.SHOW_UNCONSERVED; - value = checkTF(value); - break; - 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; - } - // put name and value into application args - if (value != null && argName != null) - { - vargs.add(argName); - if (value != "true") - { - vargs.add(value); - } - } - if (value == null) - { - value = "false"; - } - System.out.println("AppletParams propName=" + prefName + " argName=" - + argName + " appletName=" - + appletName + " value=" + value); - if (appletName != null) - { - appletParams.put(appletName, value); - } - if (prefName != null) - { - Cache.setPropertyNoSave(prefName, value); - } + // 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()); } - return appletParams; } /** @@ -396,9 +428,9 @@ public class AppletParams extends HashMap * @param value * @return "true" or null */ - private static String checkTF(String value) + private static String checkTF(Object value) { - return (value.toLowerCase() == "true" ? "true" : null); + return (("" + value).toLowerCase() == "true" ? "true" : null); } /** diff --git a/src/jalview/bin/ApplicationSingletonProvider.java b/src/jalview/bin/ApplicationSingletonProvider.java new file mode 100644 index 0000000..b64f40c --- /dev/null +++ b/src/jalview/bin/ApplicationSingletonProvider.java @@ -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 . + * 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 + *
          + *
        • the Java runtime (JVM) when running as Java
        • + *
        • one 'applet', when running as JalviewJS
        • + *
        + * This allows separation of multiple JS applets running on the same browser + * page, each with their own 'singleton' instances. + *

        + * 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. + *

        + * 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'. + *

        + * A class whose singleton should have global scope (be shared across all + * applets on a page) should not 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, 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, ApplicationSingletonI> getContextMap() + { + @SuppressWarnings("unused") + ThreadGroup g = (Platform.isJS() + ? Thread.currentThread().getThreadGroup() + : null); + Map, 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 c) + { + Map, 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 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 c) + { + Map, ApplicationSingletonI> map = getContextMap(); + if (map != null) + { + map.remove(c); + } + } +} diff --git a/src/jalview/bin/ArgsParser.java b/src/jalview/bin/ArgsParser.java index 91c8838..c5c08f6 100644 --- a/src/jalview/bin/ArgsParser.java +++ b/src/jalview/bin/ArgsParser.java @@ -20,8 +20,11 @@ */ 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, @@ -96,7 +99,7 @@ public class ArgsParser public static final String VSESS = "vsess"; - private Vector vargs = null; + private List vargs = null; private boolean isApplet; @@ -109,7 +112,7 @@ public class ArgsParser public ArgsParser(String[] args) { - vargs = new Vector<>(); + vargs = new ArrayList<>(); isApplet = (args.length > 0 && args[0].startsWith("= 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 '" @@ -1111,11 +1106,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); } } @@ -1160,17 +1155,12 @@ public class Cache { if (coloursFound.toString().length() > 1) { - /* for JalviewJS should be - - getInstance(). - - */ setProperty(UserDefinedColours.USER_DEFINED_COLOURS, coloursFound.toString()); } else { - applicationProperties + getInstance().applicationProperties .remove(UserDefinedColours.USER_DEFINED_COLOURS); } } @@ -1201,16 +1191,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); @@ -1218,7 +1212,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"), @@ -1237,10 +1233,11 @@ 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 + * For AppletParams and Preferences ok_actionPerformed and + * startupFileTextfield_mouseClicked * * Sets a property value for the running application, without saving it to the * properties file @@ -1250,12 +1247,7 @@ public class Cache */ public static void setPropertyNoSave(String key, String obj) { - /* for JalviewJS should be - - getInstance(). - - */ - setPropertyImpl(key, obj, false); + getInstance().setPropertyImpl(key, obj, false); } /** @@ -1267,7 +1259,8 @@ public class Cache * @param andSave * @return */ - private /* for JalviewJS should not be static */ static Object setPropertyImpl(String key, String obj, boolean andSave) + private Object setPropertyImpl( + String key, String obj, boolean andSave) { Object oldValue = null; try @@ -1287,6 +1280,4 @@ public class Cache return oldValue; } - - } diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index e98ea63..4a5c733 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -37,7 +37,6 @@ import java.security.PermissionCollection; import java.security.Permissions; import java.security.Policy; import java.util.HashMap; -import java.util.Hashtable; import java.util.Map; import java.util.Vector; @@ -49,23 +48,13 @@ import com.threerings.getdown.util.LaunchUtil; import groovy.lang.Binding; import groovy.util.GroovyScriptEngine; import jalview.api.AlignCalcWorkerI; -import jalview.api.AlignViewportI; -import jalview.api.JalviewApp; -import jalview.api.StructureSelectionManagerProvider; -import jalview.datamodel.ColumnSelection; -import jalview.datamodel.HiddenColumns; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceGroup; -import jalview.datamodel.SequenceI; +import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI; import jalview.ext.so.SequenceOntology; import jalview.gui.AlignFrame; import jalview.gui.AlignViewport; -import jalview.gui.AlignmentPanel; -import jalview.gui.CalculationChooser; import jalview.gui.Desktop; import jalview.gui.Preferences; import jalview.gui.PromptUserConfig; -import jalview.gui.StructureViewer; import jalview.io.AppletFormatAdapter; import jalview.io.BioJsHTMLOutput; import jalview.io.DataSourceType; @@ -78,17 +67,12 @@ import jalview.io.HtmlSvgOutput; import jalview.io.IdentifyFile; import jalview.io.NewickFile; import jalview.io.gff.SequenceOntologyFactory; -import jalview.javascript.JSFunctionExec; -import jalview.javascript.MouseOverStructureListener; -import jalview.renderer.seqfeatures.FeatureRenderer; import jalview.schemes.ColourSchemeI; import jalview.schemes.ColourSchemeProperty; -import jalview.structure.SelectionSource; -import jalview.structure.VamsasSource; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.ws.jws2.Jws2Discoverer; -import netscape.javascript.JSObject; +//import netscape.javascript.JSObject; /** * Main class for Jalview Application
        @@ -105,30 +89,44 @@ import netscape.javascript.JSObject; * @author $author$ * @version $Revision$ */ -public class Jalview implements JalviewJSApi +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); } - private boolean headless; - - // singleton instance of this class + private Jalview() + { + } - private static Jalview instance; + private boolean headless; private Desktop desktop; - public static AlignFrame currentAlignFrame; - - public boolean isJavaAppletTag; + public AlignFrame currentAlignFrame; public String appletResourcePath; - JalviewAppLoader appLoader; - - protected JSFunctionExec jsFunctionExec; + public String j2sAppletID; private boolean noCalculation, noMenuBar, noStatus; @@ -156,12 +154,9 @@ public class Jalview implements JalviewJSApi 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() @@ -241,12 +236,6 @@ public class Jalview implements JalviewJSApi } - public static Jalview getInstance() - { - return instance; - } - - private final static boolean doPlatformLogging = false; /** @@ -261,9 +250,7 @@ public class Jalview implements JalviewJSApi { Platform.startJavaLogging(); } - - instance = new Jalview(); - instance.doMain(args); + getInstance().doMain(args); } /** @@ -273,11 +260,7 @@ public class Jalview implements JalviewJSApi { boolean isJS = Platform.isJS(); - if (isJS) - { - Platform.setAppClass(this); - } - else + if (!isJS) { System.setSecurityManager(null); } @@ -305,7 +288,7 @@ public class Jalview implements JalviewJSApi } // report Jalview version - Cache.loadBuildProperties(true); + Cache.getInstance().loadBuildProperties(true); ArgsParser aparser = new ArgsParser(args); headless = false; @@ -314,18 +297,16 @@ public class Jalview implements JalviewJSApi Cache.loadProperties(usrPropsFile); // must do this before + boolean allowServices = true; + if (isJS) { - isJavaAppletTag = aparser.isApplet(); - if (isJavaAppletTag) - { - Preferences.setAppletDefaults(); - Cache.loadProperties(usrPropsFile); // again, because we - // might be changing defaults here? - } - System.out.println( - " found: " + aparser.getValue("Info.j2sAppletID")); - appletResourcePath = aparser.getValue("Info.resourcePath"); + j2sAppletID = Platform.getAppID(null); + Preferences.setAppletDefaults(); + Cache.loadProperties(usrPropsFile); // again, because we + // might be changing defaults here? + appletResourcePath = (String) aparser.getAppletValue("resourcepath", + null, true); } else /** @@ -357,11 +338,12 @@ public class Jalview implements JalviewJSApi // anything else! final String jabawsUrl = aparser.getValue(ArgsParser.JABAWS); - if (jabawsUrl != null) + 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) @@ -408,8 +390,6 @@ public class Jalview implements JalviewJSApi System.exit(0); } - desktop = null; - if (!isJS) /** @j2sIgnore */ { @@ -464,17 +444,18 @@ public class Jalview implements JalviewJSApi * 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); @@ -482,17 +463,17 @@ public class Jalview implements JalviewJSApi { System.out.println("Error setting Taskbar: " + t.getMessage()); } - desktop.setVisible(true); - if (!Platform.isJS()) + if (!isJS) /** * Java only * * @j2sIgnore */ { - desktop.startServiceDiscovery(); + if (allowServices) + desktop.startServiceDiscovery(); if (!aparser.contains("nousagestats")) { startUsageStats(desktop); @@ -544,37 +525,20 @@ public class Jalview implements JalviewJSApi BioJsHTMLOutput.updateBioJS(); } } - parseArguments(aparser, true); } /** - * Allow an outside entity to initiate the second half of argument parsing - * (only). + * Parse all command-line String[] arguments as well as all JavaScript-derived + * parameters from Info. * - * @param args - * @return null is good - */ - @Override - public Object parseArguments(String[] args) - { - - try - { - parseArguments(new ArgsParser(args), false); - return null; - } catch (Throwable t) - { - return t; - } - } - - /** + * We allow for this method to be run from JavaScript. Basically allowing + * simple scripting. * * @param aparser * @param isStartup */ - private void parseArguments(ArgsParser aparser, boolean isStartup) + public void parseArguments(ArgsParser aparser, boolean isStartup) { String groovyscript = null; // script to execute after all loading is @@ -609,18 +573,82 @@ public class Jalview implements JalviewJSApi 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) { @@ -651,54 +679,30 @@ public class Jalview implements JalviewJSApi } } } - String fileFormat = (isJavaAppletTag - ? aparser.getAppletValue("format", null) + + String fileFormat = (isJS + ? (String) aparser.getAppletValue("format", null, true) : null); protocol = AppletFormatAdapter.checkProtocol(file); try { - format = (isJavaAppletTag && fileFormat != null + format = (fileFormat != null ? FileFormats.getInstance().forName(fileFormat) : null); if (format == null) { format = new IdentifyFile().identify(file, protocol); } - format = new IdentifyFile().identify(file, protocol); } catch (FileFormatException e1) { // TODO ? } - 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!"); - } - - AlignFrame af = new FileLoader(!headless).LoadFileWaitTillLoaded(file, - protocol, format); + 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 { @@ -733,135 +737,13 @@ public class Jalview implements JalviewJSApi } setCurrentAlignFrame(af); - String data = aparser.getValue(ArgsParser.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(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 (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()) - { - System.out.println("CMD [-sortbytree] executed successfully!"); - } - } - - boolean doUpdateAnnotation = false; - /** - * we do this earlier in JalviewJS because of a complication with - * SHOWOVERVIEW - * - * For now, just fixing this in JalviewJS. - * - * - * @j2sIgnore - * - */ - { - if (aparser.contains(ArgsParser.NOANNOTATION) - || aparser.contains(ArgsParser.NOANNOTATION2)) - { - af.getViewport().setShowAnnotation(false); - if (!af.getViewport().isShowAnnotation()) - { - doUpdateAnnotation = true; - System.out - .println("CMD no-annotation executed successfully!"); - } - } - } + setFrameDependentProperties(aparser, af); - - if (aparser.contains(ArgsParser.NOSORTBYTREE)) - { - af.getViewport().setSortByTree(false); - if (!af.getViewport().getSortByTree()) - { - doUpdateAnnotation = true; - System.out - .println("CMD [-nosortbytree] executed successfully!"); - } - } - if (doUpdateAnnotation) - { // BH 2019.07.24 - af.setMenusForViewport(); - af.alignPanel.updateLayout(); - } - - data = aparser.getValue(ArgsParser.TREE, true); - if (data != null) - { - try - { - 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); - } - } - // TODO - load PDB structure(s) to alignment JAL-629 - // (associate with identical sequence in alignment, or a specified - // sequence) - if (isJavaAppletTag) + if (isJS) { - loadAppletParams(aparser, af); + jsApp.initFromParams(af); } - else if (!isJS) + else /** * Java only * @@ -880,75 +762,19 @@ public class Jalview implements JalviewJSApi groovyscript = null; } } - createOutputFiles(aparser, af, format); - while (aparser.getSize() > 0) - { - System.out.println("Unknown arg: " + aparser.nextValue()); - } - } - } - - - AlignFrame startUpAlframe = null; - // We'll only open the default file if the desktop is visible. - // And the user - // //////////////////// - - if (!isJS && !headless && file == null - && Cache.getDefault("SHOW_STARTUP_FILE", true)) - /** - * Java only - * - * @j2sIgnore - */ - { - 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? + if (!isJS || !isStartup) { + createOutputFiles(aparser, format); } } - - 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 (!isJS && groovyscript != null) { if (Cache.groovyJarsPresent()) { System.out.println("Executing script " + groovyscript); - executeGroovyScript(groovyscript, startUpAlframe); + executeGroovyScript(groovyscript, af); } else { @@ -967,67 +793,220 @@ public class Jalview implements JalviewJSApi } desktop.setInBatchMode(false); } + + if (jsApp != null) { + jsApp.callInitCallback(); + } } - + /** - * Writes an output file for each format (if any) specified in the - * command-line arguments. Supported formats are currently - *
          - *
        • png
        • - *
        • svg
        • - *
        • html
        • - *
        • biojsmsa
        • - *
        • imgMap
        • - *
        • eps
        • - *
        - * 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. + * Set general display parameters irrespective of file loading or headlessness. * * @param aparser - * @param af - * @param format */ - private void createOutputFiles(ArgsParser aparser, AlignFrame af, - FileFormatI format) + private void setDisplayParameters(ArgsParser aparser) { - String imageName = "unnamed.png"; - while (aparser.getSize() > 1) + if (aparser.contains(ArgsParser.NOMENUBAR)) { - String outputFormat = aparser.nextValue(); - String file = aparser.nextValue(); + noMenuBar = true; + System.out.println("CMD [nomenu] executed successfully!"); + } - 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")) + 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", " "); + + ColourSchemeI cs = ColourSchemeProperty.getColourScheme( + af.getViewport(), af.getViewport().getAlignment(), data); + + if (cs != null) { - File imageFile = new File(file); - imageName = imageFile.getName(); - af.createSVG(imageFile); - System.out.println("Creating SVG image: " + file); - continue; + System.out.println( + "CMD [-color " + data + "] executed successfully!"); } - else if (outputFormat.equalsIgnoreCase("html")) + 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 (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()) { - 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; + System.out.println("CMD [-sortbytree] executed successfully!"); } - else if (outputFormat.equalsIgnoreCase("biojsmsa")) - { - if (file == null) - { - System.err.println("The output html file must not be null"); - return; - } + } + + 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) + { + af.getViewport().setShowAnnotation(false); + if (!af.getViewport().isShowAnnotation()) + { + doUpdateAnnotation = true; + } + } + } + + if (aparser.contains(ArgsParser.NOSORTBYTREE)) + { + af.getViewport().setSortByTree(false); + if (!af.getViewport().getSortByTree()) + { + doUpdateAnnotation = true; + System.out + .println("CMD [-nosortbytree] executed successfully!"); + } + } + if (doUpdateAnnotation) + { // BH 2019.07.24 + af.setMenusForViewport(); + af.alignPanel.updateLayout(); + } + + data = aparser.getValue(ArgsParser.TREE, true); + if (data != null) + { + try + { + 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); + } + } + // 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 + *
          + *
        • png
        • + *
        • svg
        • + *
        • html
        • + *
        • biojsmsa
        • + *
        • imgMap
        • + *
        • eps
        • + *
        + * 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) + { + String outputFormat = aparser.nextValue(); + File imageFile; + String fname; + switch (outputFormat.toLowerCase()) + { + 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( @@ -1037,39 +1016,45 @@ public class Jalview implements JalviewJSApi e.printStackTrace(); } BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel); - bjs.exportHTML(file); - System.out.println("Creating BioJS MSA Viwer HTML file: " + file); + bjs.exportHTML(fname); + System.out.println("Creating BioJS MSA Viwer HTML file: " + fname); continue; - } - else if (outputFormat.equalsIgnoreCase("imgMap")) - { - af.createImageMap(new File(file), imageName); - System.out.println("Creating image map: " + file); + 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; - } - else if (outputFormat.equalsIgnoreCase("eps")) - { - File outputFile = new File(file); + case "imgmap": + imageFile = new File(aparser.nextValue()); + af.alignPanel.makePNGImageMap(imageFile, "unnamed.png"); System.out.println( - "Creating EPS file: " + outputFile.getAbsolutePath()); - af.createEPS(outputFile); + "Creating image map: " + imageFile.getAbsolutePath()); continue; } - - af.saveAlignment(file, format); - if (af.isSaveAlignmentSuccessful()) + if (!Platform.isJS()) /** @j2sIgnore */ { - System.out.println( - "Written alignment in " + format + " format to " + file); - } - else - { - System.out.println("Error writing file " + file + " in " + format - + " format!!"); + // skipping outputFormat? + System.out.println("Unknown arg: " + outputFormat); + fname = new File(aparser.nextValue()).getAbsolutePath(); + af.saveAlignment(fname, format); + if (af.isSaveAlignmentSuccessful()) + { + System.out.println( + "Written alignment in " + format + " format to " + fname); + } + else + { + System.out.println("Error writing file " + fname + " in " + format + + " format!!"); + } } - + break; + } + while (aparser.getSize() > 0) + { + System.out.println("Unknown arg: " + aparser.nextValue()); } - } private static void showUsage() @@ -1118,7 +1103,7 @@ public class Jalview implements JalviewJSApi /** * 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 ?" @@ -1297,813 +1282,32 @@ public class Jalview implements JalviewJSApi public static AlignFrame getCurrentAlignFrame() { - return Jalview.currentAlignFrame; + return Jalview.getInstance().currentAlignFrame; } public static void setCurrentAlignFrame(AlignFrame currentAlignFrame) { - Jalview.currentAlignFrame = currentAlignFrame; - } - - /** - * 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 = Cache.getProperty("Info.j2sAppletID"); - if (id == null) - { - id = "jalview"; - } - return id + (frameType == null ? "" : "-" + frameType); - } - - /** - * Handle all JalviewLite applet parameters - * - * @param aparser - * @param af - */ - private void loadAppletParams(ArgsParser aparser, AlignFrame af) - { - JalviewApp app = new JalviewApp() - { - - // TODO BH 2019 - // - // These are methods that are in JalviewLite that various classes call - // but are not in JalviewLiteJsApi. Or, even if they are, other classes - // call - // them to JalviewLite directly. Some may not be necessary, but they have - // to - // be at least mentioned here, or the classes calling them should - // reference - // JalviewLite itself. - - private boolean alignPDBStructures; // From JalviewLite; not implemented - - private Hashtable> jsmessages; - - private Hashtable jshashes; - - @Override - public String getParameter(String name) - { - return aparser.getAppletValue(name, null); - } - - @Override - public boolean getDefaultParameter(String name, boolean def) - { - String stn; - return ((stn = getParameter(name)) == null ? def - : "true".equalsIgnoreCase(stn)); - } - - /** - * Get the applet-like document base even though this is an application. - */ - @Override - public URL getDocumentBase() - { - return Platform.getDocumentBase(); - } - - /** - * Get the applet-like code base even though this is an application. - */ - @Override - public URL getCodeBase() - { - return Platform.getCodeBase(); - } - - @Override - public AlignViewportI getViewport() - { - return af.getViewport(); - } - - /** - * features - * - */ - @Override - public boolean parseFeaturesFile(String filename, - DataSourceType protocol) - { - return af.parseFeaturesFile(filename, protocol); - } - - /** - * scorefile - * - */ - @Override - public boolean loadScoreFile(String sScoreFile) throws IOException - { - af.loadJalviewDataFile(sScoreFile, null, null, null); - return true; - } - - /** - * annotations, jpredfile, jnetfile - * - */ - @Override - public void updateForAnnotations() - { - af.updateForAnnotations(); - } - - @Override - public void loadTree(NewickFile fin, String treeFile) - throws IOException - { - // n/a -- already done by standard Jalview command line processing - } - - @Override - public void setAlignPdbStructures(boolean defaultParameter) - { - alignPDBStructures = true; - } - - @Override - public void newStructureView(PDBEntry pdb, SequenceI[] seqs, - String[] chains, DataSourceType protocol) - { - StructureViewer.launchStructureViewer(af.alignPanel, pdb, seqs); - } - - @Override - public void setFeatureGroupState(String[] groups, boolean state) - { - af.setFeatureGroupState(groups, state); - } - - @Override - public void alignedStructureView(PDBEntry[] pdb, SequenceI[][] seqs, - String[][] chains, String[] protocols) - { - System.err.println( - "Jalview applet interface alignedStructureView not implemented"); - } - - @Override - public void newFeatureSettings() - { - System.err.println( - "Jalview applet interface newFeatureSettings not implemented"); - } - - private Vector jsExecQueue; - - @Override - public Vector getJsExecQueue(JSFunctionExec exec) - { - jsFunctionExec = exec; - return (jsExecQueue == null ? (jsExecQueue = new Vector<>()) - : jsExecQueue); - } - -// AppletContext deprecated -// -// @Override -// public AppletContext getAppletContext() -// { -// // TODO Auto-generated method stub -// return null; -// } -// - @Override - public boolean isJsfallbackEnabled() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public JSObject getJSObject() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public StructureSelectionManagerProvider getStructureSelectionManagerProvider() - { - // TODO Q: what exactly is this? BH - return null; - } - - @Override - public void updateColoursFromMouseOver(Object source, - MouseOverStructureListener mouseOverStructureListener) - { - // TODO Auto-generated method stub - - } - - @Override - public Object[] getSelectionForListener(SequenceGroup seqsel, - ColumnSelection colsel, HiddenColumns hidden, - SelectionSource source, Object alignFrame) - { - return appLoader.getSelectionForListener(getCurrentAlignFrame(), - seqsel, colsel, hidden, source, alignFrame); - } - - @Override - public String arrayToSeparatorList(String[] array) - { - return appLoader.arrayToSeparatorList(array); - } - - @Override - public Hashtable getJSHashes() - { - return (jshashes == null ? (jshashes = new Hashtable<>()) - : jshashes); - } - - @Override - public Hashtable> getJSMessages() - { - return (jsmessages == null ? (jsmessages = new Hashtable<>()) - : jsmessages); - } - - @Override - public Object getFrameForSource(VamsasSource source) - { - if (source != null) - { - AlignFrame af; - if (source instanceof jalview.gui.AlignViewport - && source == (af = getCurrentAlignFrame()).getViewport()) - { - // should be valid if it just generated an event! - return af; - } - // TODO: ensure that if '_af' is specified along with a handler - // function, then only events from that alignFrame are sent to that - // function - } - return null; - } - - @Override - public FeatureRenderer getNewFeatureRenderer(AlignViewportI vp) - { - return new jalview.gui.FeatureRenderer((AlignmentPanel) vp); - } - - }; - - appLoader = new JalviewAppLoader(true); - appLoader.load(app); - } - - /** - * - * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences() - */ - @Override - public String getSelectedSequences() - { - return getSelectedSequencesFrom(getCurrentAlignFrame()); - } - - /** - * - * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String) - */ - @Override - public String getSelectedSequences(String sep) - { - return getSelectedSequencesFrom(getCurrentAlignFrame(), sep); - } - - /** - * - * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui - * .AlignFrame) - */ - @Override - public String getSelectedSequencesFrom(AlignFrame alf) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return getSelectedSequencesFrom(alf, null); - } - - /** - * - * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui - * .AlignFrame, java.lang.String) - */ - @Override - public String getSelectedSequencesFrom(AlignFrame alf, String sep) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getSelectedSequencesFrom(alf, sep); + Jalview.getInstance().currentAlignFrame = currentAlignFrame; } - /** - * - * @see jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui - * .AlignFrame, java.lang.String) - */ - @Override - public void highlight(String sequenceId, String position, - String alignedPosition) - { - highlightIn(null, sequenceId, position, alignedPosition); - } - - @Override - public void highlightIn(AlignFrame alf, String sequenceId, - String position, String alignedPosition) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - appLoader.highlightIn(alf, sequenceId, position, alignedPosition); - } - - @Override - public void select(String sequenceIds, String columns) - { - selectIn(getCurrentAlignFrame(), sequenceIds, columns, null); - } - - @Override - public void select(String sequenceIds, String columns, String sep) - { - selectIn(null, sequenceIds, columns, sep); - } - - @Override - public void selectIn(AlignFrame alf, String sequenceIds, String columns) - { - selectIn(alf, sequenceIds, columns, null); - } - - @Override - public void selectIn(AlignFrame alf, String sequenceIds, String columns, - String sep) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - appLoader.selectIn(alf, sequenceIds, columns, sep); - } - - @Override - public String getSelectedSequencesAsAlignment(String format, - String suffix) - { - return getSelectedSequencesAsAlignmentFrom(null, format, suffix); - } - - @Override - public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf, - String format, String sep) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, sep); - } - - @Override - public String getAlignmentOrder() - { - return getAlignmentFrom(getCurrentAlignFrame(), null); - } - - @Override - public String getAlignmentOrderFrom(AlignFrame alf) - { - return getAlignmentFrom(alf, null); - } - - @Override - public String getAlignmentOrderFrom(AlignFrame alf, String sep) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getAlignmentOrderFrom(alf, sep); - } - - @Override - public String orderBy(String order, String undoName) - { - return orderBy(order, undoName, null); - } - - @Override - public String orderBy(String order, String undoName, String sep) - { - return orderAlignmentBy(getCurrentAlignFrame(), order, undoName, sep); - } - - @Override - public String orderAlignmentBy(AlignFrame alf, String order, - String undoName, String sep) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.orderAlignmentBy(alf, order, undoName, sep); - } - - @Override - public String getAlignment(String format) - { - return getAlignmentFrom(null, format, null); - } - - @Override - public String getAlignmentFrom(AlignFrame alf, String format) - { - return getAlignmentFrom(alf, format, null); - } - - @Override - public String getAlignment(String format, String suffix) - { - return getAlignmentFrom(getCurrentAlignFrame(), format, suffix); - } - - @Override - public String getAlignmentFrom(AlignFrame alf, String format, - String suffix) - { - return appLoader.getAlignmentFrom(alf, format, suffix); - } - - @Override - public void loadAnnotation(String annotation) - { - loadAnnotationFrom(getCurrentAlignFrame(), annotation); - } - - @Override - public void loadAnnotationFrom(AlignFrame alf, String annotation) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - appLoader.loadAnnotationFrom(alf, annotation); - } - - @Override - public void loadFeatures(String features, boolean autoenabledisplay) - { - loadFeaturesFrom(currentAlignFrame, features, autoenabledisplay); - } - - @Override - public boolean loadFeaturesFrom(AlignFrame alf, String features, - boolean autoenabledisplay) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.loadFeaturesFrom(alf, features, autoenabledisplay); - } - - @Override - public String getFeatures(String format) - { - return getFeaturesFrom(null, format); - } - - @Override - public String getFeaturesFrom(AlignFrame alf, String format) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getFeaturesFrom(alf, format); - } - - @Override - public String getAnnotation() - { - return getAnnotationFrom(null); - } - - @Override - public String getAnnotationFrom(AlignFrame alf) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getAnnotationFrom(alf); - } - - // @Override - // public AlignFrame newView() - // { - // return newViewFrom(null, null); - // } - // - // @Override - // public AlignFrame newView(String name) - // { - // return newViewFrom(null, name); - // } - // - // @Override - // public AlignFrame newViewFrom(AlignFrame alf) - // { - // return newViewFrom(alf, null); - // } - // - // @Override - // public AlignFrame newViewFrom(AlignFrame alf, String name) - // { - // if (alf == null) - // { - // alf = getCurrentAlignFrame(); - // } - // return appLoader.newViewFrom(alf, name); - // } - - @Override - public AlignFrame loadAlignment(String text, String title) - { - return appLoader.loadAlignment(text, AlignFrame.DEFAULT_WIDTH, - AlignFrame.DEFAULT_HEIGHT, title); - } - - @Override - public boolean addPdbFile(AlignFrame alFrame, String sequenceId, - String pdbEntryString, String pdbFile) - { - if (alFrame == null) - { - alFrame = getCurrentAlignFrame(); - } - return appLoader.addPdbFile(alFrame, sequenceId, pdbEntryString, - pdbFile); - } - - @Override - public void scrollViewToIn(AlignFrame alf, String topRow, - String leftHandColumn) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - appLoader.scrollViewToIn(alf, topRow, leftHandColumn); - } - - @Override - public void scrollViewToRowIn(AlignFrame alf, String topRow) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - appLoader.scrollViewToRowIn(alf, topRow); - } - - @Override - public void scrollViewToColumnIn(AlignFrame alf, String leftHandColumn) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - appLoader.scrollViewToColumnIn(alf, leftHandColumn); - } - - @Override - public String getFeatureGroups() - { - return getFeatureGroupsOn(null); - } - - @Override - public String getFeatureGroupsOn(AlignFrame alf) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getFeatureGroupsOn(alf); - } - - @Override - public String getFeatureGroupsOfState(boolean visible) - { - return getFeatureGroupsOfStateOn(null, visible); - } - - @Override - public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getFeatureGroupsOfStateOn(alf, visible); - } - - @Override - public void setFeatureGroupState(String groups, boolean state) - { // JalviewLite API - setFeatureGroupStateOn(null, groups, state); - } - - @Override - public void setFeatureGroupStateOn(AlignFrame alf, String groups, - boolean state) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - appLoader.setFeatureGroupStateOn(alf, groups, state); - } - - @Override - public String getSeparator() - { - return appLoader.getSeparator(); - } - - @Override - public void setSeparator(String separator) - { - appLoader.setSeparator(separator); - } - - @Override - public String getJsMessage(String messageclass, String viewId) - { - // see http://www.jalview.org/examples/jalviewLiteJs.html - return null; - } - - /** - * 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, or the string "label.you_need_at_least_n_sequences" if number - * of sequences selected is inappropriate - */ - @Override - public Object openTreePanel(AlignFrame af, String treeType, - String modelName) - { // JalviewJS api - if (af == null) - { - af = getCurrentAlignFrame(); - } - return CalculationChooser.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 - */ - @Override - public Object openPcaPanel(AlignFrame af, String modelName) - { - if (af == null) - { - af = getCurrentAlignFrame(); - } - return CalculationChooser.openPcaPanel(af, modelName, null); - } - - @Override - public String getSelectedSequencesAsAlignment(String format, - boolean suffix) - { - return getSelectedSequencesAsAlignmentFrom(null, format, suffix); - } - - @Override - public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf, - String format, boolean suffix) - { - if (alf == null) - { - alf = getCurrentAlignFrame(); - } - return appLoader.getSelectedSequencesAsAlignmentFrom(alf, format, - "" + suffix); - } - - @Override - public String arrayToSeparatorList(String[] array) - { - return appLoader.arrayToSeparatorList(array); - } - - @Override - public String[] separatorListToArray(String list) - { - return appLoader.separatorListToArray(list); - } - - //// probably not needed in JalviewJS -- From when Jmol and Jalview did not - //// have a direct connection? - - @Override - public void setMouseoverListener(String listener) - { - // TODO Auto-generated method stub - - } - - @Override - public void setMouseoverListener(AlignFrame af, String listener) - { - // TODO Auto-generated method stub - - } - - @Override - public void setSelectionListener(String listener) - { - // TODO Auto-generated method stub - - } - - @Override - public void setSelectionListener(AlignFrame af, String listener) - { - // TODO Auto-generated method stub - - } - - @Override - public void setStructureListener(String listener, String modelSet) - { - // TODO Auto-generated method stub - - } - - @Override - public void removeJavascriptListener(AlignFrame af, String listener) + + public void notifyWorker(AlignCalcWorkerI worker, String status) { - // TODO Auto-generated method stub - + // System.out.println("Jalview worker " + worker.getClass().getSimpleName() + // + " " + status); } - @Override - public void mouseOverStructure(String pdbResNum, String chain, - String pdbfile) - { - // TODO Auto-generated method stub - } + private static boolean isInteractive = true; - @Override - public void showOverview() + public static boolean isInteractive() { - currentAlignFrame.overviewMenuItem_actionPerformed(null); + return isInteractive; } - public void notifyWorker(AlignCalcWorkerI worker, String status) + public static void setInteractive(boolean tf) { - // System.out.println("Jalview worker " + worker.getClass().getSimpleName() - // + " " + status); + isInteractive = tf; } } diff --git a/src/jalview/bin/JalviewAppLoader.java b/src/jalview/bin/JalviewAppLoader.java deleted file mode 100644 index 1cdeaec..0000000 --- a/src/jalview/bin/JalviewAppLoader.java +++ /dev/null @@ -1,1500 +0,0 @@ -package jalview.bin; - -import jalview.api.JalviewApp; -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.Desktop; -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.SelectionSource; -import jalview.structure.StructureSelectionManager; -import jalview.util.HttpUtils; -import jalview.util.MessageManager; - -import java.awt.EventQueue; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; -import java.util.Vector; - -/** - * A class to load parameters for either JalviewLite or Jalview - * - * @author hansonr - * - */ -public class JalviewAppLoader -{ - - private JalviewApp app; // Jalview or JalviewJS or JalviewLite - - private boolean debug; - - String separator = "\u00AC"; // JalviewLite note: the default used to - // be '|', but many sequence IDS include - // pipes. - - public String getSeparator() - { - return separator; - } - - public void setSeparator(String separator) - { - this.separator = separator; - } - - public JalviewAppLoader(boolean debug) - { - this.debug = debug; - } - - public void load(JalviewApp app) - { - - this.app = app; - - String sep = app.getParameter("separator"); - if (sep != null) - { - if (sep.length() > 0) - { - separator = sep; - } - else - { - throw new Error(MessageManager - .getString("error.invalid_separator_parameter")); - } - } - - loadTree(); - loadScoreFile(); - loadFeatures(); - loadAnnotations(); - loadJnetFile(); - loadPdbFiles(); - callInitCallback(); - } - - /** - * Load PDBFiles if any specified by parameter(s). Returns true if loaded, - * else false. - * - * @param loaderFrame - * @return - */ - protected boolean loadPdbFiles() - { - boolean result = false; - /* - * Undocumented for 2.6 - - * related to JAL-434 - */ - - boolean doAlign = app.getDefaultParameter("alignpdbfiles", false); - app.setAlignPdbStructures(doAlign); - /* - * - * - * - * - * - */ - - // Accumulate pdbs here if they are heading for the same view (if - // alignPdbStructures is true) - Vector pdbs = new Vector<>(); - // create a lazy matcher if we're asked to - jalview.analysis.SequenceIdMatcher matcher = (app - .getDefaultParameter("relaxedidmatch", false)) - ? new jalview.analysis.SequenceIdMatcher( - app.getViewport().getAlignment() - .getSequencesArray()) - : null; - - int pdbFileCount = 0; - String param; - do - { - if (pdbFileCount > 0) - { - param = app.getParameter("PDBFILE" + pdbFileCount); - } - else - { - param = app.getParameter("PDBFILE"); - } - - if (param != null) - { - PDBEntry pdb = new PDBEntry(); - - String seqstring; - SequenceI[] seqs = null; - String[] chains = null; - - StringTokenizer st = new StringTokenizer(param, " "); - - if (st.countTokens() < 2) - { - String sequence = app.getParameter("PDBSEQ"); - if (sequence != null) - { - seqs = new SequenceI[] { matcher == null - ? (Sequence) app.getViewport().getAlignment() - .findName(sequence) - : matcher.findIdMatch(sequence) }; - } - - } - else - { - param = st.nextToken(); - List tmp = new ArrayList<>(); - List 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) app.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(app, 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) app) - .registerPDBEntry(pdb); - } - else - { - if (debug) - { - // this may not really be a problem but we give a warning - // anyway - System.err.println( - "Warning: Possible input parsing error: Null sequence for attachment of PDB (sequence " - + i + ")"); - } - } - } - - if (doAlign) - { - pdbs.addElement(new Object[] { pdb, seqs, chains, protocol }); - } - else - { - app.newStructureView(pdb, seqs, chains, protocol); - } - } - } - - pdbFileCount++; - } while (param != null || pdbFileCount < 10); - if (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.elementAt(pdbsi); - pdb[pdbsi] = (PDBEntry) o[0]; - seqs[pdbsi] = (SequenceI[]) o[1]; - chains[pdbsi] = (String[]) o[2]; - protocols[pdbsi] = (String) o[3]; - } - app.alignedStructureView(pdb, seqs, chains, protocols); - result = true; - } - return result; - } - - /** - * Load in a Jnetfile if specified by parameter. Returns true if loaded, else - * false. - * - * @param alignFrame - * @return - */ - protected boolean loadJnetFile() - { - boolean result = false; - String param = app.getParameter("jnetfile"); - if (param == null) - { - // jnet became jpred around 2016 - param = app.getParameter("jpredfile"); - } - if (param != null) - { - try - { - ret[0] = param; - DataSourceType protocol = resolveFileProtocol(app, ret); - JPredFile predictions = new JPredFile(ret[0], protocol); - JnetAnnotationMaker.add_annotation(predictions, - app.getViewport().getAlignment(), 0, false); - // false == do not add sequence profile from concise output - app.getViewport().getAlignment().setupJPredAlignment(); - app.updateForAnnotations(); - result = true; - } catch (Exception ex) - { - ex.printStackTrace(); - } - } - return result; - } - - /** - * Load annotations if specified by parameter. Returns true if loaded, else - * false. - * - * @param alignFrame - * @return - */ - protected boolean loadAnnotations() - { - boolean result = false; - String param = app.getParameter("annotations"); - if (param != null) - { - ret[0] = param; - DataSourceType protocol = resolveFileProtocol(app, ret); - param = ret[0]; - if (new AnnotationFile().annotateAlignmentView(app.getViewport(), - param, protocol)) - { - app.updateForAnnotations(); - result = true; - } - else - { - System.err - .println("Annotations were not added from annotation file '" - + param + "'"); - } - } - return result; - } - - /** - * Load features file and view settings as specified by parameters. Returns - * true if features were loaded, else false. - * - * @param alignFrame - * @return - */ - protected boolean loadFeatures() - { - boolean result = false; - // /////////////////////////// - // 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 = app.getParameter("hidefeaturegroups"); - if (param != null) - { - app.setFeatureGroupState(separatorListToArray(param, separator), - false); - // app.setFeatureGroupStateOn(newAlignFrame, param, false); - } - // show specific groups - param = app.getParameter("showfeaturegroups"); - if (param != null) - { - app.setFeatureGroupState(separatorListToArray(param, separator), - true); - // app.setFeatureGroupStateOn(newAlignFrame, param, true); - } - // and now load features - param = app.getParameter("features"); - if (param != null) - { - ret[0] = param; - DataSourceType protocol = resolveFileProtocol(app, ret); - - result = app.parseFeaturesFile(ret[0], protocol); - } - - param = app.getParameter("showFeatureSettings"); - if (param != null && param.equalsIgnoreCase("true")) - { - app.newFeatureSettings(); - } - return result; - } - - /** - * Load a score file if specified by parameter. Returns true if file was - * loaded, else false. - * - * @param loaderFrame - */ - protected boolean loadScoreFile() - { - boolean result = false; - String sScoreFile = app.getParameter("scoreFile"); - if (sScoreFile != null && !"".equals(sScoreFile)) - { - try - { - if (debug) - { - System.err.println( - "Attempting to load T-COFFEE score file from the scoreFile parameter"); - } - result = app.loadScoreFile(sScoreFile); - if (!result) - { - 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 result; - } - - String[] ret = new String[1]; - - /** - * Load a tree for the alignment if specified by parameter. Returns true if a - * tree was loaded, else false. - * - * @param loaderFrame - * @return - */ - protected boolean loadTree() - { - boolean result = false; - String treeFile = app.getParameter("tree"); - if (treeFile == null) - { - treeFile = app.getParameter("treeFile"); - } - - if (treeFile != null) - { - try - { - ret[0] = treeFile; - NewickFile fin = new NewickFile(treeFile, - resolveFileProtocol(app, ret)); - fin.parse(); - - if (fin.getTree() != null) - { - app.loadTree(fin, ret[0]); - result = true; - if (debug) - { - System.out.println("Successfully imported tree."); - } - } - else - { - if (debug) - { - System.out.println( - "Tree parameter did not resolve to a valid tree."); - } - } - } catch (Exception ex) - { - ex.printStackTrace(); - } - } - return result; - } - - /** - * 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 - */ - public 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 - */ - public 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 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(); - // if (debug) - // { - // System.err.println("Array from '" + separator - // + "' separated List:\n" + v.length); - // for (int i = 0; i < v.length; i++) - // { - // System.err.println("item " + i + " '" + v[i] + "'"); - // } - // } - return v; - } - // if (debug) - // { - // System.err.println( - // "Empty Array from '" + separator + "' separated List"); - // } - return null; - } - - public static DataSourceType resolveFileProtocol(JalviewApp app, - 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 = app.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 = app.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(app.getClass(), path)) - { - return DataSourceType.CLASSLOADER; - } - 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); - // if (debug) - // { - // System.err.println("Resource '" + f + "' was " - // + (rtn ? "" : "not ") + "located by classloader."); - // } - return rtn; - } catch (Exception ex) - { - System.out.println("Exception checking resources: " + f + " " + ex); - return false; - } - } - - public void callInitCallback() - { - String initjscallback = app.getParameter("oninit"); - if (initjscallback == null) - { - return; - } - initjscallback = initjscallback.trim(); - if (initjscallback.length() > 0) - { - // TODO - } - } - - /** - * read sequence1...sequenceN as a raw alignment - * - * @param jalviewApp - * @return - */ - public String getPastedSequence(JalviewApp jalviewApp) - { - StringBuffer data = new StringBuffer("PASTE"); - int i = 1; - String file = null; - while ((file = app.getParameter("sequence" + i)) != null) - { - data.append(file.toString() + "\n"); - i++; - } - if (data.length() > 5) - { - file = data.toString(); - } - return file; - } - - /** - * concatenate the list with separator - * - * @param list - * @param separator - * @return concatenated string - */ - public static String arrayToSeparatorList(String[] list, String separator) - { - // TODO use StringUtils version - StringBuffer v = new StringBuffer(); - if (list != null && list.length > 0) - { - for (int i = 0, iSize = list.length; i < iSize; i++) - { - if (list[i] != null) - { - if (i > 0) - { - v.append(separator); - } - v.append(list[i]); - } - } - // if (debug) - // { - // System.err - // .println("Returning '" + separator + "' separated List:\n"); - // System.err.println(v); - // } - return v.toString(); - } - // if (debug) - // { - // System.err.println( - // "Returning empty '" + separator + "' separated List\n"); - // } - return "" + separator; - } - - public String arrayToSeparatorList(String[] array) - { - return arrayToSeparatorList(array, separator); - } - - public String getSelectedSequencesFrom(AlignFrame alf, String sep) - { - StringBuffer result = new StringBuffer(""); - if (sep == null || sep.length() == 0) - { - sep = separator; // "+0x00AC; - } - AlignViewport v = alf.getViewport(); - if (v.getSelectionGroup() != null) - { - SequenceI[] seqs = v.getSelectionGroup() - .getSequencesInOrder(v.getAlignment()); - - for (int i = 0; i < seqs.length; i++) - { - result.append(seqs[i].getName()); - result.append(sep); - } - } - - return result.toString(); - } - - public void setFeatureGroupStateOn(final AlignFrame alf, - final String groups, boolean state) - { - java.awt.EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - alf.setFeatureGroupState( - separatorListToArray(groups, separator), state); - } - }); - } - - public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible) - { - return arrayToSeparatorList( - alf.getFeatureGroupsOfState(visible)); - } - - public void scrollViewToIn(final AlignFrame alf, final String topRow, - final String leftHandColumn) - { - // TODO test - java.awt.EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - try - { - alf.scrollTo(Integer.valueOf(topRow).intValue(), - Integer.valueOf(leftHandColumn).intValue()); - - } catch (Exception ex) - { - System.err.println("Couldn't parse integer arguments (topRow='" - + topRow + "' and leftHandColumn='" + leftHandColumn - + "')"); - ex.printStackTrace(); - } - } - }); - } - - public void scrollViewToRowIn(final AlignFrame alf, final String topRow) - { - // TODO test - - java.awt.EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - try - { - alf.scrollToRow(Integer.valueOf(topRow).intValue()); - - } catch (Exception ex) - { - System.err.println("Couldn't parse integer arguments (topRow='" - + topRow + "')"); - ex.printStackTrace(); - } - - } - }); - } - - public void scrollViewToColumnIn(final AlignFrame alf, - final String leftHandColumn) - { - // TODO test - java.awt.EventQueue.invokeLater(new Runnable() - { - - @Override - public void run() - { - try - { - alf - .scrollToColumn(Integer.valueOf(leftHandColumn).intValue()); - - } catch (Exception ex) - { - System.err.println( - "Couldn't parse integer arguments (leftHandColumn='" - + leftHandColumn + "')"); - ex.printStackTrace(); - } - } - }); - - } - - public boolean addPdbFile(AlignFrame alf, String sequenceId, - String pdbEntryString, String pdbFile) - { - AlignFrame alFrame = alf; - SequenceI toaddpdb = alFrame.getViewport().getAlignment() - .findName(sequenceId); - boolean needtoadd = false; - if (toaddpdb != null) - { - Vector pdbe = toaddpdb.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(pdbEntryString) - && !pdbentry.getFile().equals(pdbFile)) - { - pdbentry = null; - } - else - { - continue; - } - } - } - if (pdbentry == null) - { - pdbentry = new PDBEntry(); - pdbentry.setId(pdbEntryString); - pdbentry.setFile(pdbFile); - needtoadd = true; // add this new entry to sequence. - } - // resolve data source - // TODO: this code should be a refactored to an io package - DataSourceType protocol = AppletFormatAdapter.resolveProtocol(pdbFile, - FileFormat.PDB); - if (protocol == null) - { - return false; - } - if (needtoadd) - { - pdbentry.setProperty("protocol", protocol); - toaddpdb.addPDBId(pdbentry); - alFrame.alignPanel.getStructureSelectionManager() - .registerPDBEntry(pdbentry); - } - } - return true; - } - - public AlignFrame loadAlignment(String text, int width, int height, - String title) - { - 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, height, title); - } - } catch (IOException ex) - { - ex.printStackTrace(); - } - return null; - } - - public String getFeatureGroupsOn(AlignFrame alf) - { - return arrayToSeparatorList( - alf.getFeatureGroups()); - } - - public void highlightIn(final AlignFrame alf, final String sequenceId, - final String position, final String alignedPosition) - { - // 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); - } - }); - } - } - } - - public void selectIn(final AlignFrame alf, String sequenceIds, - String columns, String sep) - { - if (sep == null || sep.length() == 0) - { - sep = separator; - } - else - { - if (debug) - { - System.err.println("Selecting region using separator string '" - + separator + "'"); - } - } - // deparse fields - String[] ids = JalviewAppLoader.separatorListToArray(sequenceIds, sep); - String[] cols = JalviewAppLoader.separatorListToArray(columns, sep); - 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); - } - } - if (debug) - { - System.err.println("Range '" + cl + "' deparsed as [" + from - + "," + to + "]"); - } - } - 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); - if (debug) - { - System.err.println("Point selection '" + cl - + "' deparsed as [" + 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 cs = new ArrayList<>(csel.getSelected()); - csel.clear(); - for (Integer selectedCol : cs) - { - csel.addElement(rs.findIndex(selectedCol)); - } - } - sel.setStartRes(start); - sel.setEndRes(end); - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - alf.select(sel, csel, alf - .getCurrentView().getAlignment().getHiddenColumns()); - } - }); - } - } - - public String getAlignmentOrderFrom(AlignFrame alf, String sep) - { - 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 arrayToSeparatorList(order, sep); - } - - public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf, - String format, String suffix) - { - try - { - AlignViewport vp = alf.getViewport(); - FileFormatI theFormat = FileFormats.getInstance().forName(format); - boolean seqlimits = (suffix == null - || suffix.equalsIgnoreCase("true")); - 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()), - seqlimits); - return reply; - } - } catch (IllegalArgumentException ex) - { - ex.printStackTrace(); - return "Error retrieving alignment, possibly invalid format specifier: " - + format; - } - return ""; - } - - public String orderAlignmentBy(AlignFrame alf, String order, - String undoName, String sep) - { - if (sep == null || sep.length() == 0) - { - sep = separator; - } - String[] ids = JalviewAppLoader.separatorListToArray(order, sep); - 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 ""; - } - ; - 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 after - // this has returned. - return alf.sortBy(aorder, _undoName) ? "true" : ""; - } - - public String getAlignmentFrom(AlignFrame alf, String format, - String suffix) - { - try - { - boolean seqlimits = (suffix == null - || suffix.equalsIgnoreCase("true")); - - FileFormatI theFormat = FileFormats.getInstance().forName(format); - String reply = new AppletFormatAdapter().formatSequences(theFormat, - alf.getViewport().getAlignment(), seqlimits); - return reply; - } catch (IllegalArgumentException ex) - { - ex.printStackTrace(); - return "Error retrieving alignment, possibly invalid format specifier: " - + format; - } - } - - public void loadAnnotationFrom(AlignFrame alf, String annotation) - { - if (new AnnotationFile().annotateAlignmentView( - alf.getViewport(), annotation, - DataSourceType.PASTE)) - { - alf.alignPanel.fontChanged(); - alf.alignPanel.setScrollValues(0, 0); - } - else - { - alf.parseFeaturesFile(annotation, - DataSourceType.PASTE); - } - } - - public boolean loadFeaturesFrom(AlignFrame alf, String features, - boolean autoenabledisplay) - { - 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; - } - - /** - * JavaScript interface to print the alignment frame - * - * @param alf - * @param format - * "jalview" or "gff" with or without ";includeComplement" or - * ";includeNonpositional"; default with no ";" is - * ";includeNonpositional" - * @return - */ - public String getFeaturesFrom(AlignFrame alf, String format) - { - AlignFrame f = (alf); - - 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( - f.getViewport().getAlignment().getSequencesArray(), - f.alignPanel.getFeatureRenderer(), nonpos, compl); - } - else - { - features = formatter.printGffFormat( - f.getViewport().getAlignment().getSequencesArray(), - f.alignPanel.getFeatureRenderer(), nonpos, compl); - } - - if (features == null) - { - features = ""; - } - return features; - - } - - public String getAnnotationFrom(AlignFrame alf) - { - AlignFrame f = alf; - String annotation = new AnnotationFile() - .printAnnotationsForView(f.getViewport()); - return annotation; - } - - // public AlignFrame newViewFrom(AlignFrame alf, String name) - // { - // return (AlignFrame) alf.newView(name, true); - // } - // - public String[] separatorListToArray(String list) - { - return separatorListToArray(list, separator); - } - - public Object[] getSelectionForListener(AlignFrame currentFrame, - SequenceGroup seqsel, ColumnSelection colsel, - HiddenColumns hidden, SelectionSource source, Object alignFrame) - { - // System.err.println("Testing selection event relay to - // jsfunction:"+_listener); - String setid = ""; - AlignFrame src = (AlignFrame) alignFrame; - if (source != null) - { - if (source instanceof AlignViewport - && currentFrame.getViewport() == source) - { - // should be valid if it just generated an event! - src = currentFrame; - - } - } - String[] seqs = new String[] {}; - String[] cols = new String[] {}; - int strt = 0, end = (src == null) ? -1 - : src.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[2]; - cols[0] = "" + (1 + strt) + "-" + (1 + end); - } - } - return new Object[] { src, setid, arrayToSeparatorList(seqs), - arrayToSeparatorList(cols) }; - } - -} \ No newline at end of file diff --git a/src/jalview/bin/JalviewJS2.java b/src/jalview/bin/JalviewJS2.java index 6f69f8a..61b3487 100644 --- a/src/jalview/bin/JalviewJS2.java +++ b/src/jalview/bin/JalviewJS2.java @@ -36,11 +36,11 @@ public class JalviewJS2 args = new String[] { // "headless", "open", "examples/uniref50.fa", - "features", - "examples/exampleFeatures.txt" - , "noannotation" - , "showoverview" - // , "png", "test-bh.png" +// "features", +// "examples/exampleFeatures.txt" +// , "noannotation" + //, "showoverview" + //, "png", "test-bh.png" }; } diff --git a/src/jalview/bin/JalviewJSApi.java b/src/jalview/bin/JalviewJSApi.java deleted file mode 100644 index a21bebe..0000000 --- a/src/jalview/bin/JalviewJSApi.java +++ /dev/null @@ -1,693 +0,0 @@ -package jalview.bin; - -import jalview.gui.AlignFrame; - -/** - * JAL-3369 JalviewJS API BH 2019.07.17 - * - * @author hansonr - * - */ -public interface JalviewJSApi -{ - - void showOverview(); - - /** - * process commandline arguments after the JavaScript application has started - * - * @param args - * @return - */ - Object parseArguments(String[] args); - - - /** - * Open a new Tree panel on the desktop statically. Params are standard (not - * set by Groovy). No dialog is opened. - * - * @param af - * may be null - * @param treeType - * @param modelName - * @return null, or the string "label.you_need_at_least_n_sequences" if number - * of sequences selected is inappropriate - */ - public Object openTreePanel(AlignFrame af, String treeType, - String modelName); - - /** - * public static method for JalviewJS API to open a PCAPanel without - * necessarily using a dialog. - * - * @param af - * may be null - * @param modelName - * @return the PCAPanel, or the string "label.you_need_at_least_n_sequences" - * if number of sequences selected is inappropriate - */ - public Object openPcaPanel(AlignFrame af, String modelName); - - /** - * The following public methods may be called externally, eg via javascript in - * an HTML page. - * - *
        - * TODO: introduce abstract interface for - * jalview.appletgui.AlignFrame
        - * - * Most function arguments are strings, which contain serialised versions of - * lists. Lists of things are separated by a separator character - either the - * default or a user supplied one. Ranges and positions on an alignment or - * sequence can be specified as a list, where an item containing a single - * number is a single position, and an item like 1-2 specifies columns 1 and 2 - * as a range. - */ - - // /** - // * @author jimp - // * - // */ - // public interface JalviewLiteJsApi - // { - - /** - * @return String list of selected sequence IDs, each terminated by the - * 'boolean not' character (""+0x00AC); or (¬); - */ - - public abstract String getSelectedSequences(); - - /** - * @param sep - * separator string or null for default - * @return String list of selected sequence IDs, each terminated by given - * separator string - */ - - public abstract String getSelectedSequences(String sep); - - /** - * @param alf - * AlignFrame containing selection - * @return String list of selected sequence IDs, each terminated by current - * default separator sequence - * - */ - public abstract String getSelectedSequencesFrom(AlignFrame alf); - - /** - * get list of selected sequence IDs separated by given separator - * - * @param alf - * window containing selection - * @param sep - * separator string to use - default is 'boolean not' - * @return String list of selected sequence IDs, each terminated by the given - * separator - */ - public abstract String getSelectedSequencesFrom(AlignFrame alf, - String sep); - - /** - * - * @param sequenceId - * id of sequence to highlight - * @param position - * integer position [ tobe implemented or range ] on sequence - * @param alignedPosition - * true/false/empty string - indicate if position is an alignment - * column or unaligned sequence position - */ - - public abstract void highlight(String sequenceId, String position, - String alignedPosition); - - /** - * - * @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 - */ - public abstract void highlightIn(AlignFrame alf, String sequenceId, - String position, String alignedPosition); - - /** - * select regions of the currrent alignment frame - * - * @param sequenceIds - * String separated list of sequence ids or empty string - * @param columns - * String separated list { column range or column, ..} or empty - * string - */ - - public abstract void select(String sequenceIds, String columns); - - /** - * select regions of the currrent alignment frame - * - * @param toselect - * String separated list { column range, seq1...seqn sequence ids } - * @param sep - * separator between toselect fields - */ - - public abstract void select(String sequenceIds, String columns, - String sep); - - /** - * select regions of the given alignment frame - * - * @param alf - * @param toselect - * String separated list { column range, seq1...seqn sequence ids } - * @param sep - * separator between toselect fields - */ - public abstract void selectIn(AlignFrame alf, String sequenceIds, - String columns); - - /** - * select regions of the given alignment frame - * - * @param alf - * @param toselect - * String separated list { column range, seq1...seqn sequence ids } - * @param sep - * separator between toselect fields - */ - public abstract void selectIn(AlignFrame alf, String sequenceIds, - String columns, String sep); - - /** - * get sequences selected in current AlignFrame and return their alignment in - * format 'format' either with or without suffix - * - * @param alf - * - where selection is - * @param format - * - format of alignment file - * @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 abstract String getSelectedSequencesAsAlignment(String format, - String suffix); - - /** - * get sequences selected in alf and return their alignment in format 'format' - * either with or without suffix - * - * @param alf - * - where selection is - * @param format - * - format of alignment file - * @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 abstract String getSelectedSequencesAsAlignmentFrom( - AlignFrame alf, - String format, String suffix); - - /** - * get a separator separated list of sequence IDs reflecting the order of the - * current alignment - * - * @return - */ - - public abstract String getAlignmentOrder(); - - /** - * get a separator separated list of sequence IDs reflecting the order of the - * alignment in alf - * - * @param alf - * @return - */ - public abstract String getAlignmentOrderFrom(AlignFrame alf); - - /** - * get a sep separated list of sequence IDs reflecting the order of the - * alignment in alf - * - * @param alf - * @param sep - * - separator to use - * @return - */ - public abstract String getAlignmentOrderFrom(AlignFrame alf, - String sep); - - /** - * re-order the current alignment using the given list of sequence IDs - * - * @param order - * - sep separated list - * @param undoName - * - string to use when referring to ordering action in undo buffer - * @return 'true' if alignment was actually reordered. empty string if - * alignment did not contain sequences. - */ - - public abstract String orderBy(String order, String undoName); - - /** - * re-order the current alignment using the given list of sequence IDs - * separated by sep - * - * @param order - * - sep separated list - * @param undoName - * - string to use when referring to ordering action in undo buffer - * @param sep - * @return 'true' if alignment was actually reordered. empty string if - * alignment did not contain sequences. - */ - - public abstract String orderBy(String order, String undoName, - String sep); - - /** - * re-order the given alignment using the given list of sequence IDs separated - * by sep - * - * @param alf - * @param order - * - sep separated list - * @param undoName - * - string to use when referring to ordering action in undo buffer - * @param sep - * @return 'true' if alignment was actually reordered. empty string if - * alignment did not contain sequences. - */ - public abstract String orderAlignmentBy(AlignFrame alf, String order, - String undoName, String sep); - - /** - * get alignment as format (format names FASTA, BLC, CLUSTAL, MSF, PILEUP, - * PFAM - see jalview.io.AppletFormatAdapter for full list); - * - * @param format - * @return - */ - - public abstract String getAlignment(String format); - - /** - * get alignment displayed in alf as format - * - * @param alf - * @param format - * @return - */ - public abstract String getAlignmentFrom(AlignFrame alf, String format); - - /** - * get alignment as format with jalview start-end sequence suffix appended - * - * @param format - * @param suffix - * @return - */ - - public abstract String getAlignment(String format, String suffix); - - /** - * get alignment displayed in alf as format with or without the jalview - * start-end sequence suffix appended - * - * @param alf - * @param format - * @param suffix - * @return - */ - public abstract String getAlignmentFrom(AlignFrame alf, String format, - String suffix); - - /** - * add the given features or annotation to the current alignment - * - * @param annotation - */ - - public abstract void loadAnnotation(String annotation); - - /** - * add the given features or annotation to the given alignment view - * - * @param alf - * @param annotation - */ - public abstract void loadAnnotationFrom(AlignFrame alf, - String annotation); - - /** - * parse the given string as a jalview feature or GFF annotation file and - * optionally enable feature display on the current 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. - */ - - public abstract void loadFeatures(String features, - boolean autoenabledisplay); - - /** - * parse the given string as a jalview feature or GFF annotation file and - * optionally enable feature display on the given AlignFrame. - * - * @param alf - * @param features - * - gff or features file - * @param autoenabledisplay - * - when true, feature display will be enabled if any features can - * be parsed from the string. - * @return true if data parsed as features - */ - public abstract boolean loadFeaturesFrom(AlignFrame alf, String features, - boolean autoenabledisplay); - - /** - * get the sequence features in the given format (Jalview or GFF); - * - * @param format - * @return - */ - - public abstract String getFeatures(String format); - - /** - * get the sequence features in alf in the given format (Jalview or GFF); - * - * @param alf - * @param format - * @return - */ - public abstract String getFeaturesFrom(AlignFrame alf, String format); - - /** - * get current alignment's annotation as an annotation file - * - * @return - */ - - public abstract String getAnnotation(); - - /** - * get alignment view alf's annotation as an annotation file - * - * @param alf - * @return - */ - public abstract String getAnnotationFrom(AlignFrame alf); - - // BH incompatibility here -- JalviewLite created an AlignFrame; Jalview - // creates an AlignmentPanel - // /** - // * create a new view and return the AlignFrame instance - // * - // * @return - // */ - // - // public abstract AlignFrame newView(); - // - // /** - // * create a new view named name and return the AlignFrame instance - // * - // * @param name - // * @return - // */ - // - // public abstract AlignFrame newView(String name); - // - // /** - // * create a new view on alf and return the AlignFrame instance - // * - // * @param alf - // * @return - // */ - // public abstract AlignFrame newViewFrom(AlignFrame alf); - // - // /** - // * create a new view named name on alf - // * - // * @param alf - // * @param name - // * @return - // */ - // public abstract AlignFrame newViewFrom(AlignFrame alf, String name); - - /** - * - * @param text - * alignment file as a string - * @param title - * window title - * @return null or new alignment frame - */ - - public abstract AlignFrame loadAlignment(String text, String title); - - /** - * register a javascript function to handle any alignment mouseover events - * - * @param listener - * name of javascript function (called with arguments - * [jalview.appletgui.AlignFrame,String(sequence id);,String(column - * in alignment);, String(position in sequence);] - */ - - public abstract void setMouseoverListener(String listener); - - /** - * register a javascript function to handle mouseover events - * - * @param af - * (null or specific AlignFrame for which events are to be listened - * for); - * @param listener - * name of javascript function - */ - public abstract void setMouseoverListener(AlignFrame af, - String listener); - - /** - * register a javascript function to handle any alignment selection events. - * Events are generated when the user completes a selection event, or when the - * user deselects all selected regions. - * - * @param listener - * name of javascript function (called with arguments - * [jalview.appletgui.AlignFrame, String(sequence set id);, - * String(separator separated list of sequences which were - * selected);, String(separator separated list of column ranges (i.e. - * single number or hyphenated range); that were selected);] - */ - - public abstract void setSelectionListener(String listener); - - public abstract void setSelectionListener(AlignFrame af, - String listener); - - /** - * register a javascript function to handle events normally routed to a Jmol - * structure viewer. - * - * @param listener - * - javascript function (arguments are variable, see - * jalview.javascript.MouseOverStructureListener for full details); - * @param modelSet - * - 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 - */ - - public abstract void setStructureListener(String listener, - String modelSet); - - /** - * remove any callback using the given listener function and associated with - * the given AlignFrame (or null for all callbacks); - * - * @param af - * (may be null); - * @param listener - * (may be null); - */ - public abstract void removeJavascriptListener(AlignFrame af, - String listener); - - /** - * send a mouseover message to all the alignment windows associated with the - * given residue in the pdbfile - * - * @param pdbResNum - * @param chain - * @param pdbfile - */ - - public abstract void mouseOverStructure(String pdbResNum, String chain, - String pdbfile); - - /** - * bind a pdb file to a sequence in the given AlignFrame. - * - * @param alFrame - * - null or specific AlignFrame. This specifies the dataset that - * will be searched for a seuqence called sequenceId - * @param sequenceId - * - sequenceId within the dataset. - * @param pdbEntryString - * - the short name for the PDB file - * @param pdbFile - * - pdb file - either a URL or a valid PDB file. - * @return true if binding was as success TODO: consider making an exception - * structure for indicating when PDB parsing or sequenceId location - * fails. - */ - public abstract boolean addPdbFile(AlignFrame alFrame, String sequenceId, - String pdbEntryString, String pdbFile); - - /** - * adjust horizontal/vertical scroll to make the given location the top left - * hand corner for the given view - * - * @param alf - * @param topRow - * @param leftHandColumn - */ - public abstract void scrollViewToIn(AlignFrame alf, String topRow, - String leftHandColumn); - - /** - * adjust vertical scroll to make the given row the top one for given view - * - * @param alf - * @param topRow - */ - public abstract void scrollViewToRowIn(AlignFrame alf, String topRow); - - /** - * adjust horizontal scroll to make the given column the left one in the given - * view - * - * @param alf - * @param leftHandColumn - */ - public abstract void scrollViewToColumnIn(AlignFrame alf, - String leftHandColumn); - - /** - * - * @return - * @see jalview.appletgui.AlignFrame#getFeatureGroups(); - */ - - public abstract String getFeatureGroups(); - - /** - * @param alf - * AlignFrame to get feature groups on - * @return - * @see jalview.appletgui.AlignFrame#getFeatureGroups(); - */ - public abstract String getFeatureGroupsOn(AlignFrame alf); - - /** - * @param visible - * @return - * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean); - */ - - public abstract String getFeatureGroupsOfState(boolean visible); - - /** - * @param alf - * align frame to get groups of state visible - * @param visible - * @return - * @see jalview.appletgui.AlignFrame#getFeatureGroupsOfState(boolean); - */ - public abstract String getFeatureGroupsOfStateOn(AlignFrame alf, - boolean visible); - - /** - * @param groups - * tab separated list of group names - * @param state - * true or false - * @see jalview.appletgui.AlignFrame#setFeatureGroupState(java.lang.String[], - * boolean); - */ - public abstract void setFeatureGroupStateOn(AlignFrame alf, - String groups, - boolean state); - - - public abstract void setFeatureGroupState(String groups, boolean state); - - /** - * List separator string - * - * @return the separator - */ - - public abstract String getSeparator(); - - /** - * List separator string - * - * @param separator - * the separator to set. empty string will reset separator to default - */ - - public abstract void setSeparator(String separator); - - /** - * Retrieve fragments of a large packet of data made available by JalviewLite. - * - * @param messageclass - * @param viewId - * @return next chunk of message - */ - - public abstract String getJsMessage(String messageclass, String viewId); - - /// in http://www.jalview.org/examples/jalviewLiteJs.html but missing here - - // get selected sequences as alignment as format with or without start-end - // suffix - - public String getSelectedSequencesAsAlignment(String format, - boolean suffix); - - // get selected sequences as alignment from given view as format with or - // without start-end suffix - public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf, - String format, boolean suffix); - - - public String arrayToSeparatorList(String[] array); - - // get a string array from a list - - public String[] separatorListToArray(String list); - - // debug flag - controls output to standard out - public static boolean debug = false; - -} diff --git a/src/jalview/bin/JalviewJSApp.java b/src/jalview/bin/JalviewJSApp.java new file mode 100644 index 0000000..feab4ae --- /dev/null +++ b/src/jalview/bin/JalviewJSApp.java @@ -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 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 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 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 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 list = alf.getViewport().getAlignment() + .getSequences(); + List tmp = new ArrayList(); + 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 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) + { + + /* + * Undocumented for 2.6 - + * related to JAL-434 + */ + + // not supported (as for JalviewLite) + // boolean doAlign = false;//"true".equalsIgnoreCase("" + + // getAppletParameter("alignpdbfiles", false)); + // setAlignPdbStructures(doAlign); + /* + * + * + * + * + * + */ + + // Accumulate pdbs here if they are heading for the same view (if + // alignPdbStructures is true) + // ArrayList 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 tmp = new ArrayList<>(); + List 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(); + } +} diff --git a/src/jalview/bin/JalviewLite.java b/src/jalview/bin/JalviewLite.java index e7f2a53..6a8db59 100644 --- a/src/jalview/bin/JalviewLite.java +++ b/src/jalview/bin/JalviewLite.java @@ -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 javascriptListeners = new Vector<>(); + private Vector 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 diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index c4098e2..8d81a34 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -591,7 +591,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; diff --git a/src/jalview/datamodel/PDBEntry.java b/src/jalview/datamodel/PDBEntry.java index c1dc77c..d39e9f1 100755 --- a/src/jalview/datamodel/PDBEntry.java +++ b/src/jalview/datamodel/PDBEntry.java @@ -21,6 +21,7 @@ package jalview.datamodel; import jalview.util.CaseInsensitiveString; +import jalview.ws.params.InvalidArgumentException; import java.util.Collections; import java.util.Enumeration; @@ -152,6 +153,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 +280,12 @@ public class PDBEntry return id; } + /** + * TODO + * + * @param key "protocol" + * @param value + */ public void setProperty(String key, Object value) { if (this.properties == null) diff --git a/src/jalview/datamodel/features/FeatureAttributes.java b/src/jalview/datamodel/features/FeatureAttributes.java index bcf404b..a57fd55 100644 --- a/src/jalview/datamodel/features/FeatureAttributes.java +++ b/src/jalview/datamodel/features/FeatureAttributes.java @@ -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 atts = attributes.get(featureType); if (atts == null) { diff --git a/src/jalview/datamodel/features/FeatureSources.java b/src/jalview/datamodel/features/FeatureSources.java index b316821..4a87349 100644 --- a/src/jalview/datamodel/features/FeatureSources.java +++ b/src/jalview/datamodel/features/FeatureSources.java @@ -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 sources; - /** - * Answers the singleton instance of this class - * - * @return - */ public static FeatureSources getInstance() { - return instance; + return (FeatureSources) ApplicationSingletonProvider + .getInstance(FeatureSources.class); } + private Map sources; + private FeatureSources() { sources = new HashMap<>(); diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java index 8ac4991..82772bc 100644 --- a/src/jalview/datamodel/features/SequenceFeatures.java +++ b/src/jalview/datamodel/features/SequenceFeatures.java @@ -372,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)) diff --git a/src/jalview/ext/ensembl/EnsemblCds.java b/src/jalview/ext/ensembl/EnsemblCds.java index 8f13d99..bf37265 100644 --- a/src/jalview/ext/ensembl/EnsemblCds.java +++ b/src/jalview/ext/ensembl/EnsemblCds.java @@ -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; diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index 157b8b9..3d957f0 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -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) diff --git a/src/jalview/ext/ensembl/EnsemblSeqProxy.java b/src/jalview/ext/ensembl/EnsemblSeqProxy.java index fd8800f..11f8b7b 100644 --- a/src/jalview/ext/ensembl/EnsemblSeqProxy.java +++ b/src/jalview/ext/ensembl/EnsemblSeqProxy.java @@ -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); } } diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 453152e..cfb30b3 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -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 { diff --git a/src/jalview/ext/so/SequenceOntology.java b/src/jalview/ext/so/SequenceOntology.java index 0d631e6..89b2415 100644 --- a/src/jalview/ext/so/SequenceOntology.java +++ b/src/jalview/ext/so/SequenceOntology.java @@ -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 + "'"); } diff --git a/src/jalview/fts/service/pdb/PDBFTSRestClient.java b/src/jalview/fts/service/pdb/PDBFTSRestClient.java index 22ed591..c6860cc 100644 --- a/src/jalview/fts/service/pdb/PDBFTSRestClient.java +++ b/src/jalview/fts/service/pdb/PDBFTSRestClient.java @@ -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 allDefaultDisplayedStructureDataColumns; public Collection getAllDefaultDisplayedStructureDataColumns() diff --git a/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java b/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java index 3f0b8a4..0d860c4 100644 --- a/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java +++ b/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java @@ -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() { diff --git a/src/jalview/gui/AlignExportOptions.java b/src/jalview/gui/AlignExportOptions.java index 70601c9..3a8fb7c 100644 --- a/src/jalview/gui/AlignExportOptions.java +++ b/src/jalview/gui/AlignExportOptions.java @@ -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()); } /** diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 29f6a2e..14e09e7 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -20,59 +20,6 @@ */ package jalview.gui; -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.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.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 ext.vamsas.ServiceHandle; import jalview.analysis.AlignmentSorter; import jalview.analysis.AlignmentUtils; import jalview.analysis.CrossRef; @@ -156,6 +103,60 @@ import jalview.ws.jws2.Jws2Discoverer; import jalview.ws.jws2.jabaws2.Jws2Instance; import jalview.ws.seqfetcher.DbSourceProxy; +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.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.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 ext.vamsas.ServiceHandle; + /** * DOCUMENT ME! * @@ -167,6 +168,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener { + public static int frameCount; + public static final int DEFAULT_WIDTH = 700; public static final int DEFAULT_HEIGHT = 500; @@ -194,6 +197,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, File fileObject; + private int id; + /** * Creates a new AlignFrame object with specific width and height. * @@ -288,6 +293,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width, int height, String sequenceSetId, String viewId) { + + id = (++frameCount); + setSize(width, height); if (al.getDataset() == null) @@ -297,9 +305,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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(); } @@ -319,8 +327,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { viewport.hideSequence(hiddenSeqs); } - alignPanel = new AlignmentPanel(this, viewport); - addAlignmentPanel(alignPanel, true); + // alignPanel = new AlignmentPanel(this, viewport); + // addAlignmentPanel(alignPanel, true); init(); } @@ -336,7 +344,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { viewport = ap.av; alignPanel = ap; - addAlignmentPanel(ap, false); + // addAlignmentPanel(ap, false); init(); } @@ -344,13 +352,39 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * initalise the alignframe from the underlying viewport data and the * configurations */ + void init() { -// setBackground(Color.white); // BH 2019 - + + 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, @@ -365,7 +399,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // 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")) @@ -377,8 +411,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, sortPairwiseMenuItem_actionPerformed(null); } - this.alignPanel.av - .setShowAutocalculatedAbove(isShowAutoCalculatedAbove()); + // BH see above + // + // this.alignPanel.av + // .setShowAutocalculatedAbove(isShowAutoCalculatedAbove()); setMenusFromViewport(viewport); buildSortByAnnotationScoresMenu(); @@ -393,7 +429,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, }); buildColourMenu(); - if (Desktop.desktop != null) + if (Desktop.getDesktopPane() != null) { this.setDropTarget(new java.awt.dnd.DropTarget(this, this)); if (!Platform.isJS()) @@ -408,7 +444,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, wrapMenuItem_actionPerformed(null); } - if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false)) + if (jalview.bin.Cache.getDefault(Preferences.SHOW_OVERVIEW, false)) { this.overviewMenuItem_actionPerformed(null); } @@ -492,6 +528,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } addFocusListener(new FocusAdapter() { + @Override public void focusGained(FocusEvent e) { @@ -510,6 +547,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param format * format of file */ + public void setFileName(String file, FileFormatI format) { fileName = file; @@ -523,6 +561,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param file */ + public void setFileObject(File file) { this.fileObject = file; @@ -532,10 +571,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * Add a KeyListener with handlers for various KeyPressed and KeyReleased * events */ + void addKeyListener() { addKeyListener(new KeyAdapter() { + @Override public void keyPressed(KeyEvent evt) { @@ -552,8 +593,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, switch (evt.getKeyCode()) { - case 27: // escape key - deselectAllSequenceMenuItem_actionPerformed(null); + case KeyEvent.VK_ESCAPE: // escape key + // alignPanel.deselectAllSequences(); + alignPanel.deselectAllSequences(); break; @@ -736,16 +778,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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; } @@ -791,9 +831,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { ap.av.getAlignment().padGaps(); } - ap.av.updateConservation(ap); - ap.av.updateConsensus(ap); - ap.av.updateStrucConsensus(ap); + if (Jalview.getInstance().getStartCalculations()) + { + ap.av.updateConservation(ap); + ap.av.updateConsensus(ap); + ap.av.updateStrucConsensus(ap); + } } } @@ -816,9 +859,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, private void addServiceListeners() { final java.beans.PropertyChangeListener thisListener; - Desktop.instance.addJalviewPropertyChangeListener("services", + Desktop.getInstance().addJalviewPropertyChangeListener("services", thisListener = new java.beans.PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent evt) { @@ -842,19 +886,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, }); addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() { + @Override public void internalFrameClosed( javax.swing.event.InternalFrameEvent evt) { // System.out.println("deregistering discoverer listener"); - Desktop.instance.removeJalviewPropertyChangeListener("services", - thisListener); + Desktop.getInstance().removeJalviewPropertyChangeListener( + "services", thisListener); closeMenuItem_actionPerformed(true); } }); // Finally, build the menu once to get current service state new Thread(new Runnable() { + @Override public void run() { @@ -867,6 +913,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * Configure menu items that vary according to whether the alignment is * nucleotide or protein */ + public void setGUINucleotide() { AlignmentI al = getViewport().getAlignment(); @@ -891,6 +938,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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() { @@ -904,6 +952,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param av * AlignViewport */ + public void setMenusFromViewport(AlignViewport av) { padGapsMenuitem.setSelected(av.isPadGaps()); @@ -921,13 +970,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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()); @@ -945,7 +989,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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); @@ -960,6 +1005,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param b */ + public void setGroovyEnabled(boolean b) { runGroovy.setEnabled(b); @@ -972,6 +1018,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long) */ + @Override public void setProgressBar(String message, long id) { @@ -989,6 +1036,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @return true if any progress bars are still active */ + @Override public boolean operationInProgress() { @@ -1000,6 +1048,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * will cause the status bar to be hidden, with possibly undesirable flicker * of the screen layout. */ + @Override public void setStatus(String text) { @@ -1009,6 +1058,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /* * Added so Castor Mapping file can obtain Jalview Version */ + public String getVersion() { return jalview.bin.Cache.getProperty("VERSION"); @@ -1028,103 +1078,106 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void addFromFile_actionPerformed(ActionEvent e) { - Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport); + Desktop.getInstance().inputLocalFileMenuItem_actionPerformed(viewport); } @Override public void reload_actionPerformed(ActionEvent e) { - if (fileName != null) + if (fileName == null) { - // 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)) + 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)) + { + 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, fileName, 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) + { + + DataSourceType protocol = (fileName.startsWith("http:") + ? DataSourceType.URL + : DataSourceType.FILE); + 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 @@ -1145,6 +1198,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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() { @@ -1168,7 +1222,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // 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"), @@ -1199,6 +1253,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @return true if last call to saveAlignment(file, format) was successful. */ + public boolean isSaveAlignmentSuccessful() { @@ -1231,6 +1286,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param file * @param format */ + public void saveAlignment(String file, FileFormatI format) { lastSaveSuccessful = true; @@ -1242,21 +1298,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, String shortName = title; if (shortName.indexOf(File.separatorChar) > -1) { - shortName = shortName.substring( - shortName.lastIndexOf(File.separatorChar) + 1); + shortName = shortName + .substring(shortName.lastIndexOf(File.separatorChar) + 1); } - lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file, shortName); - + lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file, + shortName); + statusBar.setText(MessageManager.formatMessage( "label.successfully_saved_to_file_in_format", new Object[] { fileName, format })); - + return; } AlignExportSettingsI options = new AlignExportSettingsAdapter(false); Runnable cancelAction = new Runnable() { + @Override public void run() { @@ -1265,6 +1323,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, }; Runnable outputAction = new Runnable() { + @Override public void run() { @@ -1287,16 +1346,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, BackupFiles backupfiles = doBackup ? new BackupFiles(file) : null; try { - String tempFilePath = doBackup ? backupfiles.getTempFilePath() : file; - PrintWriter out = new PrintWriter( - new FileWriter(tempFilePath)); + String tempFilePath = doBackup ? backupfiles.getTempFilePath() + : file; + PrintWriter out = new PrintWriter(new FileWriter(tempFilePath)); out.print(output); out.close(); AlignFrame.this.setTitle(file); statusBar.setText(MessageManager.formatMessage( - "label.successfully_saved_to_file_in_format", new Object[] - { fileName, format.getName() })); + "label.successfully_saved_to_file_in_format", + new Object[] + { fileName, format.getName() })); lastSaveSuccessful = true; } catch (Exception ex) { @@ -1338,6 +1398,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param fileFormatName */ + @Override protected void outputText_actionPerformed(String fileFormatName) { @@ -1346,6 +1407,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignExportSettingsI options = new AlignExportSettingsAdapter(false); Runnable outputAction = new Runnable() { + @Override public void run() { @@ -1396,6 +1458,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void htmlMenuItem_actionPerformed(ActionEvent e) { @@ -1410,6 +1473,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, bjs.exportHTML(null); } + // ?? + public void createImageMap(File file, String image) { alignPanel.makePNGImageMap(file, image); @@ -1421,6 +1486,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param f */ + @Override public void createPNG(File f) { @@ -1433,6 +1499,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param f */ + @Override public void createEPS(File f) { @@ -1445,6 +1512,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param f */ + @Override public void createSVG(File f) { @@ -1464,6 +1532,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void printMenuItem_actionPerformed(ActionEvent e) { @@ -1490,11 +1559,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, final JalviewFileChooser chooser = new JalviewFileChooser( jalview.bin.Cache.getProperty("LAST_DIRECTORY")); chooser.setFileView(new JalviewFileView()); - String tooltip = MessageManager.getString("label.load_jalview_annotations"); + String tooltip = MessageManager + .getString("label.load_jalview_annotations"); chooser.setDialogTitle(tooltip); chooser.setToolTipText(tooltip); chooser.setResponseHandler(0, new Runnable() { + @Override public void run() { @@ -1513,6 +1584,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param closeAllTabs */ + @Override public void closeMenuItem_actionPerformed(boolean closeAllTabs) { @@ -1567,6 +1639,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param panelToClose */ + public void closeView(AlignmentPanel panelToClose) { int index = tabbedPane.getSelectedIndex(); @@ -1590,6 +1663,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * DOCUMENT ME! */ + void updateEditMenuBar() { @@ -1643,6 +1717,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @return alignment objects for all views */ + AlignmentI[] getViewAlignments() { if (alignPanels != null) @@ -1668,6 +1743,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void undoMenuItem_actionPerformed(ActionEvent e) { @@ -1695,8 +1771,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // && viewport.getColumnSelection().getHiddenColumns() != null && // viewport.getColumnSelection() // .getHiddenColumns().size() > 0); - originalSource.firePropertyChange("alignment", null, - originalSource.getAlignment().getSequences()); + originalSource.notifyAlignment(); + } } @@ -1706,6 +1782,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void redoMenuItem_actionPerformed(ActionEvent e) { @@ -1735,8 +1812,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // && viewport.getColumnSelection().getHiddenColumns() != null && // viewport.getColumnSelection() // .getHiddenColumns().size() > 0); - originalSource.firePropertyChange("alignment", null, - originalSource.getAlignment().getSequences()); + originalSource.notifyAlignment(); + } } @@ -1788,6 +1865,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param up * DOCUMENT ME! */ + public void moveSelectedSequences(boolean up) { SequenceGroup sg = viewport.getSelectionGroup(); @@ -1912,6 +1990,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void copy_actionPerformed() { @@ -1934,16 +2013,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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); @@ -1963,7 +2043,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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[] @@ -1976,6 +2056,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void pasteNew_actionPerformed(ActionEvent e) { @@ -1988,6 +2069,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void pasteThis_actionPerformed(ActionEvent e) { @@ -2000,6 +2082,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param newAlignment * true to paste to a new alignment, otherwise add to this. */ + void paste(boolean newAlignment) { boolean externalPaste = true; @@ -2035,12 +2118,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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++) @@ -2065,10 +2150,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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 { @@ -2084,8 +2169,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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 newDs = (importDs) ? new Vector<>() : null; // used to @@ -2266,8 +2351,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } buildSortByAnnotationScoresMenu(); } - viewport.firePropertyChange("alignment", null, - alignment.getSequences()); + viewport.notifyAlignment(); if (alignPanels != null) { for (AlignmentPanel ap : alignPanels) @@ -2287,10 +2371,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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); } @@ -2342,11 +2425,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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); } @@ -2385,6 +2467,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Action Cut (delete and copy) the selected region */ + @Override protected void cut_actionPerformed() { @@ -2395,6 +2478,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Performs menu option to Delete the currently selected region */ + @Override protected void delete_actionPerformed() { @@ -2405,56 +2489,63 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, return; } - Runnable okAction = new Runnable() - { - @Override - public void run() - { - SequenceI[] cut = sg.getSequences() - .toArray(new SequenceI[sg.getSize()]); - - addHistoryItem(new EditCommand( - MessageManager.getString("label.cut_sequences"), Action.CUT, - cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1, - viewport.getAlignment())); - - viewport.setSelectionGroup(null); - viewport.sendSelection(); - viewport.getAlignment().deleteGroup(sg); - - viewport.firePropertyChange("alignment", null, - viewport.getAlignment().getSequences()); - if (viewport.getAlignment().getHeight() < 1) - { - try - { - AlignFrame.this.setClosed(true); - } catch (Exception ex) - { - } - } - }}; + Runnable okAction = new Runnable() + { + + @Override + public void run() + { + SequenceI[] cut = sg.getSequences() + .toArray(new SequenceI[sg.getSize()]); + + addHistoryItem(new EditCommand( + MessageManager.getString("label.cut_sequences"), Action.CUT, + cut, sg.getStartRes(), + sg.getEndRes() - sg.getStartRes() + 1, + viewport.getAlignment())); + + viewport.setSelectionGroup(null); + viewport.sendSelection(); + viewport.getAlignment().deleteGroup(sg); + + viewport.notifyAlignment(); + + if (viewport.getAlignment().getHeight() < 1) + { + try + { + AlignFrame.this.setClosed(true); + } catch (Exception ex) + { + } + } + } + }; /* * If the cut affects all sequences, prompt for confirmation */ - boolean wholeHeight = sg.getSize() == viewport.getAlignment().getHeight(); + boolean wholeHeight = sg.getSize() == viewport.getAlignment() + .getHeight(); boolean wholeWidth = (((sg.getEndRes() - sg.getStartRes()) + 1) == viewport.getAlignment().getWidth()) ? true : false; - if (wholeHeight && wholeWidth) - { - JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop); - dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION - Object[] options = new Object[] { MessageManager.getString("action.ok"), - MessageManager.getString("action.cancel") }; - dialog.showDialog(MessageManager.getString("warn.delete_all"), - MessageManager.getString("label.delete_all"), - JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null, - options, options[0]); - } else - { - okAction.run(); - } + if (wholeHeight && wholeWidth) + { + JvOptionPane dialog = JvOptionPane + .newOptionDialog(Desktop.getDesktopPane()); + dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION + Object[] options = new Object[] { + MessageManager.getString("action.ok"), + MessageManager.getString("action.cancel") }; + dialog.showDialog(MessageManager.getString("warn.delete_all"), + MessageManager.getString("label.delete_all"), + JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null, + options, options[0]); + } + else + { + okAction.run(); + } } /** @@ -2463,6 +2554,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void deleteGroups_actionPerformed(ActionEvent e) { @@ -2480,21 +2572,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @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(); } /** @@ -2503,24 +2585,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @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(); } /** @@ -2529,6 +2598,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void invertSequenceMenuItem_actionPerformed(ActionEvent e) { @@ -2536,7 +2606,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (sg == null) { - selectAllSequenceMenuItem_actionPerformed(null); + alignPanel.selectAllSequences(); return; } @@ -2568,6 +2638,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void remove2LeftMenuItem_actionPerformed(ActionEvent e) { @@ -2580,6 +2651,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void remove2RightMenuItem_actionPerformed(ActionEvent e) { @@ -2626,8 +2698,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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); @@ -2641,8 +2713,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } } - viewport.firePropertyChange("alignment", null, - viewport.getAlignment().getSequences()); + viewport.notifyAlignment(); + } } @@ -2652,6 +2724,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e) { @@ -2676,8 +2749,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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 @@ -2691,8 +2764,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // if (viewport.hasHiddenColumns) // viewport.getColumnSelection().compensateForEdits(shifts); ranges.setStartRes(seq.findIndex(startRes) - 1); - viewport.firePropertyChange("alignment", null, - viewport.getAlignment().getSequences()); + viewport.notifyAlignment(); + } @@ -2702,6 +2775,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void removeAllGapsMenuItem_actionPerformed(ActionEvent e) { @@ -2729,9 +2803,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment())); viewport.getRanges().setStartRes(seq.findIndex(startRes) - 1); - - viewport.firePropertyChange("alignment", null, - viewport.getAlignment().getSequences()); + viewport.notifyAlignment(); } @@ -2741,12 +2813,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void padGapsMenuitem_actionPerformed(ActionEvent e) { viewport.setPadGaps(padGapsMenuitem.isSelected()); - viewport.firePropertyChange("alignment", null, - viewport.getAlignment().getSequences()); + viewport.notifyAlignment(); + } /** @@ -2755,6 +2828,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void findMenuItem_actionPerformed(ActionEvent e) { @@ -2764,6 +2838,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Create a new view of the current alignment. */ + @Override public void newView_actionPerformed(ActionEvent e) { @@ -2779,6 +2854,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * if true then duplicate all annnotation, groups and settings * @return new alignment panel, already displayed. */ + public AlignmentPanel newView(String viewTitle, boolean copyAnnotation) { /* @@ -2799,8 +2875,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (viewport.getViewName() == null) { - viewport.setViewName(MessageManager - .getString("label.view_name_original")); + viewport.setViewName( + MessageManager.getString("label.view_name_original")); } /* @@ -2851,6 +2927,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param viewTitle * @return */ + protected String getNewViewName(String viewTitle) { int index = Desktop.getViewCount(viewport.getSequenceSetId()); @@ -2885,6 +2962,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param comps * @return */ + protected List getExistingViewNames(List comps) { List existingNames = new ArrayList<>(); @@ -2905,6 +2983,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Explode tabbed views into separate windows. */ + @Override public void expandViews_actionPerformed(ActionEvent e) { @@ -2914,10 +2993,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * 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); } /** @@ -2926,6 +3006,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void font_actionPerformed(ActionEvent e) { @@ -2938,6 +3019,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void seqLimit_actionPerformed(ActionEvent e) { @@ -2967,6 +3049,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed() */ + @Override protected void followHighlight_actionPerformed() { @@ -2988,6 +3071,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void colourTextMenuItem_actionPerformed(ActionEvent e) { @@ -3001,6 +3085,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void wrapMenuItem_actionPerformed(ActionEvent e) { @@ -3037,6 +3122,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param toggleSeqs * @param toggleCols */ + protected void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols) { @@ -3104,6 +3190,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt. * event.ActionEvent) */ + @Override public void hideAllButSelection_actionPerformed(ActionEvent e) { @@ -3118,6 +3205,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event * .ActionEvent) */ + @Override public void hideAllSelection_actionPerformed(ActionEvent e) { @@ -3137,6 +3225,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event. * ActionEvent) */ + @Override public void showAllhidden_actionPerformed(ActionEvent e) { @@ -3168,6 +3257,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void scaleAbove_actionPerformed(ActionEvent e) { @@ -3182,6 +3272,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void scaleLeft_actionPerformed(ActionEvent e) { @@ -3196,6 +3287,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void scaleRight_actionPerformed(ActionEvent e) { @@ -3210,6 +3302,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void viewBoxesMenuItem_actionPerformed(ActionEvent e) { @@ -3223,6 +3316,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void viewTextMenuItem_actionPerformed(ActionEvent e) { @@ -3236,6 +3330,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void renderGapsMenuItem_actionPerformed(ActionEvent e) { @@ -3281,6 +3376,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param evt * DOCUMENT ME! */ + @Override public void showSeqFeatures_actionPerformed(ActionEvent evt) { @@ -3297,16 +3393,32 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @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 @@ -3326,7 +3438,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, JLabel textLabel = new JLabel(); textLabel.setText(content); textLabel.setBackground(Color.WHITE); - + pane = new JPanel(new BorderLayout()); ((JPanel) pane).setOpaque(true); pane.setBackground(Color.WHITE); @@ -3360,6 +3472,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void overviewMenuItem_actionPerformed(ActionEvent e) { @@ -3369,12 +3482,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } JInternalFrame frame = new JInternalFrame(); - - // BH 2019.07.26 we allow for an embedded // undecorated overview with defined size - frame.setName(Jalview.getAppID("overview")); + frame.setName(Platform.getAppID("overview")); // Dimension dim = Platform.getDimIfEmbedded(frame, -1, -1); if (dim != null && dim.width == 0) @@ -3399,13 +3510,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, "resize") != "none"); Desktop.addInternalFrame(frame, MessageManager .formatMessage("label.overview_params", new Object[] - { this.getTitle() }), true, dim.width, dim.height, resizable, - 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) @@ -3434,6 +3546,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation * ()[0])); } */ + @Override public void annotationColour_actionPerformed() { @@ -3453,6 +3566,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param selected */ + @Override public void applyToAllGroups_actionPerformed(boolean selected) { @@ -3465,6 +3579,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param name * the name (not the menu item label!) of the colour scheme */ + @Override public void changeColour_actionPerformed(String name) { @@ -3482,8 +3597,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * otherwise set the chosen colour scheme (or null for 'None') */ ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name, - viewport, - viewport.getAlignment(), viewport.getHiddenRepSequences()); + viewport, viewport.getAlignment(), + viewport.getHiddenRepSequences()); changeColour(cs); } @@ -3492,6 +3607,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param cs */ + @Override public void changeColour(ColourSchemeI cs) { @@ -3506,6 +3622,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Show the PID threshold slider panel */ + @Override protected void modifyPID_actionPerformed() { @@ -3517,6 +3634,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Show the Conservation slider panel */ + @Override protected void modifyConservation_actionPerformed() { @@ -3528,6 +3646,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Action on selecting or deselecting (Colour) By Conservation */ + @Override public void conservationMenuItem_actionPerformed(boolean selected) { @@ -3549,6 +3668,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Action on selecting or deselecting (Colour) Above PID Threshold */ + @Override public void abovePIDThreshold_actionPerformed(boolean selected) { @@ -3577,6 +3697,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void sortPairwiseMenuItem_actionPerformed(ActionEvent e) { @@ -3594,6 +3715,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void sortIDMenuItem_actionPerformed(ActionEvent e) { @@ -3610,6 +3732,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void sortLengthMenuItem_actionPerformed(ActionEvent e) { @@ -3626,6 +3749,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void sortGroupMenuItem_actionPerformed(ActionEvent e) { @@ -3643,6 +3767,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void removeRedundancyMenuItem_actionPerformed(ActionEvent e) { @@ -3655,6 +3780,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e) { @@ -3680,11 +3806,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @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(); } } @@ -3710,6 +3839,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param options * parameters for the distance or similarity calculation */ + void newTreePanel(String type, String modelName, SimilarityParamsI options) { @@ -3727,7 +3857,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { 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( @@ -3771,6 +3901,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param order * DOCUMENT ME! */ + public void addSortByOrderMenuItem(String title, final AlignmentOrder order) { @@ -3780,6 +3911,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, sort.add(item); item.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(ActionEvent e) { @@ -3806,6 +3938,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * the label used to retrieve scores for each sequence on the * alignment */ + public void addSortByAnnotScoreMenuItem(JMenu sort, final String scoreLabel) { @@ -3813,6 +3946,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, sort.add(item); item.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(ActionEvent e) { @@ -3838,6 +3972,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * rebuilding in subsequence calls. * */ + @Override public void buildSortByAnnotationScoresMenu() { @@ -3867,8 +4002,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, Enumeration labels = scoreSorts.keys(); while (labels.hasMoreElements()) { - addSortByAnnotScoreMenuItem(sortByAnnotScore, - labels.nextElement()); + addSortByAnnotScoreMenuItem(sortByAnnotScore, labels.nextElement()); } sortByAnnotScore.setVisible(scoreSorts.size() > 0); scoreSorts.clear(); @@ -3879,41 +4013,38 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** + * 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 treePanels = getTreePanels(); + sortByTreeMenu.setEnabled(!treePanels.isEmpty()); + } + + /** * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a * TreePanel with an appropriate jalview.analysis.AlignmentSorter * 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 comps = PaintRefresher.components - .get(viewport.getSequenceSetId()); - List 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 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) { @@ -3927,6 +4058,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } } + private List getTreePanels() + { + List comps = PaintRefresher.components + .get(viewport.getSequenceSetId()); + List 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(); @@ -3945,6 +4091,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * be submitted for multiple alignment. * */ + public jalview.datamodel.AlignmentView gatherSequencesForAlignment() { // Now, check we have enough sequences @@ -3990,6 +4137,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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; @@ -4023,6 +4171,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param e * DOCUMENT ME! */ + @Override protected void loadTreeMenuItem_actionPerformed(ActionEvent e) { @@ -4035,8 +4184,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, chooser.setToolTipText( MessageManager.getString("label.load_tree_file")); - chooser.setResponseHandler(0,new Runnable() + chooser.setResponseHandler(0, new Runnable() { + @Override public void run() { @@ -4050,7 +4200,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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); @@ -4058,7 +4209,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } if (fin != null && fin.hasWarningMessage()) { - JvOptionPane.showMessageDialog(Desktop.desktop, + JvOptionPane.showMessageDialog(Desktop.getDesktopPane(), fin.getWarningMessage(), MessageManager.getString( "label.possible_problem_with_tree_file"), @@ -4100,6 +4251,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * position * @return TreePanel handle */ + public TreePanel showNewickTree(NewickFile nf, String treeTitle, AlignmentView input, int w, int h, int x, int y) { @@ -4145,6 +4297,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * Generates menu items and listener event actions for web service clients * */ + public void BuildWebServiceMenu() { while (buildingMenu) @@ -4161,6 +4314,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, buildingMenu = true; new Thread(new Runnable() { + @Override public void run() { @@ -4195,9 +4349,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, final JMenu dismenu = new JMenu("Protein Disorder"); // JAL-940 - only show secondary structure prediction services from // the legacy server + Hashtable> ds = Discoverer + .getInstance().getServices(); if (// Cache.getDefault("SHOW_JWS1_SERVICES", true) // && - Discoverer.services != null && (Discoverer.services.size() > 0)) + ds != null && (ds.size() > 0)) { // TODO: refactor to allow list of AbstractName/Handler bindings to // be @@ -4205,15 +4361,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // No MSAWS used any more: // Vector msaws = null; // (Vector) // Discoverer.services.get("MsaWS"); - Vector secstrpr = Discoverer.services - .get("SecStrPred"); + Vector secstrpr = ds.get("SecStrPred"); if (secstrpr != null) { // Add any secondary structure prediction services for (int i = 0, j = secstrpr.size(); i < j; i++) { - final ext.vamsas.ServiceHandle sh = secstrpr - .get(i); + final ext.vamsas.ServiceHandle sh = secstrpr.get(i); jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer .getServiceClient(sh); int p = secstrmenu.getItemCount(); @@ -4238,6 +4392,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { @@ -4257,10 +4412,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, webService.add(me.webServiceNoServices); } // TODO: move into separate menu builder class. - boolean new_sspred = false; + // boolean new_sspred = false; if (Cache.getDefault("SHOW_JWS2_SERVICES", true)) { - Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer(); + Jws2Discoverer jws2servs = Jws2Discoverer.getInstance(); if (jws2servs != null) { if (jws2servs.hasServices()) @@ -4322,6 +4477,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param webService */ + protected void build_urlServiceMenu(JMenu webService) { // TODO: remove this code when 2.7 is released @@ -4330,7 +4486,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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); } @@ -4359,6 +4515,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @return true if Show Cross-references menu should be enabled */ + public boolean canShowProducts() { SequenceI[] seqs = viewport.getAlignment().getSequencesArray(); @@ -4386,6 +4543,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, JMenuItem xtype = new JMenuItem(source); xtype.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { @@ -4418,6 +4576,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param source * the database to show cross-references for */ + protected void showProductsFor(final SequenceI[] sel, final boolean _odna, final String source) { @@ -4429,6 +4588,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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) { @@ -4447,8 +4607,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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) @@ -4457,8 +4617,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, "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 { @@ -4486,6 +4646,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param format */ + public void setFileFormat(FileFormatI format) { this.currentFileFormat = format; @@ -4500,11 +4661,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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)); } @@ -4550,184 +4712,190 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); Transferable t = evt.getTransferable(); - final AlignFrame thisaf = this; final List files = new ArrayList<>(); List 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 files, + List 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 filesmatched = new ArrayList<>(); + ArrayList filesnotmatched = new ArrayList<>(); + for (int i = 0; i < files.size(); i++) { - @Override - public void run() + // 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) { - try + 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) { - // 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 filesmatched = new ArrayList<>(); - ArrayList filesnotmatched = new ArrayList<>(); - for (int i = 0; i < files.size(); i++) + 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(file, 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, - "" + MessageManager.formatMessage( - "label.ignore_unmatched_dropped_files_info", - new Object[] - { Integer.valueOf( - filesnotmatched.size()) - .toString() }) - + "", - MessageManager.getString( - "label.ignore_unmatched_dropped_files"), - JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION)) - { - return; - } - for (Object fn : filesnotmatched) + 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); + } + } + 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, + "" + MessageManager.formatMessage( + "label.ignore_unmatched_dropped_files_info", + new Object[] + { Integer.valueOf(filesnotmatched.size()) + .toString() }) + + "", + 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(); } } @@ -4743,6 +4911,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param file * either a filename or a URL string. */ + public void loadJalviewDataFile(Object file, DataSourceType sourceType, FileFormatI format, SequenceI assocSeq) { @@ -4784,7 +4953,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { // 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") @@ -4881,7 +5050,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, + (format != null ? "(parsing as '" + format + "' file)" : ""), - oom, Desktop.desktop); + oom, Desktop.getDesktopPane()); } } @@ -4907,6 +5076,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param state * visible or invisible */ + public void setFeatureGroupState(String[] groups, boolean state) { jalview.api.FeatureRenderer fr = null; @@ -4924,11 +5094,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } } - /** * 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) { @@ -4999,6 +5169,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * On right mouse click on view tab, prompt for and set new view name. */ + @Override public void tabbedPane_mousePressed(MouseEvent e) { @@ -5025,6 +5196,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Open the dialog for regex description parsing. */ + @Override protected void extractScores_actionPerformed(ActionEvent e) { @@ -5048,6 +5220,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent * ) */ + @Override protected void showDbRefs_actionPerformed(ActionEvent e) { @@ -5060,6 +5233,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event. * ActionEvent) */ + @Override protected void showNpFeats_actionPerformed(ActionEvent e) { @@ -5072,6 +5246,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param av */ + public boolean closeView(AlignViewportI av) { if (viewport == av) @@ -5115,6 +5290,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, Cache.getDefault(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, true)); trimrs.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { @@ -5136,6 +5312,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { new Thread(new Runnable() { + @Override public void run() { @@ -5147,6 +5324,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, alignPanel.alignFrame.featureSettings, isNucleotide); dbRefFetcher.addListener(new FetchFinishedListenerI() { + @Override public void finished() { @@ -5170,16 +5348,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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 otherdb; JMenu dfetch = new JMenu(); @@ -5203,9 +5386,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } 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() { @@ -5230,6 +5412,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, dbRefFetcher .addListener(new FetchFinishedListenerI() { + @Override public void finished() { @@ -5264,6 +5447,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { src.getDbSource() })); fetchr.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { @@ -5284,6 +5468,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, dbRefFetcher .addListener(new FetchFinishedListenerI() { + @Override public void finished() { @@ -5352,6 +5537,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, dbRefFetcher .addListener(new FetchFinishedListenerI() { + @Override public void finished() { @@ -5403,23 +5589,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * 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 @@ -5436,6 +5622,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java. * awt.event.ActionEvent) */ + @Override protected void showUnconservedMenuItem_actionPerformed(ActionEvent e) { @@ -5450,6 +5637,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event * .ActionEvent) */ + @Override protected void showGroupConsensus_actionPerformed(ActionEvent e) { @@ -5465,6 +5653,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt * .event.ActionEvent) */ + @Override protected void showGroupConservation_actionPerformed(ActionEvent e) { @@ -5479,6 +5668,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt * .event.ActionEvent) */ + @Override protected void showConsensusHistogram_actionPerformed(ActionEvent e) { @@ -5493,6 +5683,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt * .event.ActionEvent) */ + @Override protected void showSequenceLogo_actionPerformed(ActionEvent e) { @@ -5522,6 +5713,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt * .event.ActionEvent) */ + @Override protected void makeGrpsFromSelection_actionPerformed(ActionEvent e) { @@ -5573,6 +5765,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param alignmentPanel */ + public void setDisplayedView(AlignmentPanel alignmentPanel) { if (!viewport.getSequenceSetId() @@ -5597,6 +5790,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param forAlignment * update non-sequence-related annotations */ + @Override protected void setAnnotationsVisibility(boolean visible, boolean forSequences, boolean forAlignment) @@ -5630,6 +5824,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Store selected annotation sort order for the view and repaint. */ + @Override protected void sortAnnotations_actionPerformed() { @@ -5643,6 +5838,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @return alignment panels in this alignment frame */ + public List getAlignPanels() { // alignPanels is never null @@ -5654,6 +5850,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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 @@ -5704,6 +5901,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @param show */ + @Override protected void showComplement_actionPerformed(boolean show) { @@ -5718,6 +5916,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * Generate the reverse (optionally complemented) of the selected sequences, * and add them to the alignment */ + @Override protected void showReverse_actionPerformed(boolean complement) { @@ -5743,6 +5942,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to * be targeted at this alignment. */ + @Override protected void runGroovy_actionPerformed() { @@ -5756,7 +5956,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } 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); @@ -5776,6 +5976,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * @param columnsContaining * @return */ + public boolean hideFeatureColumns(String featureType, boolean columnsContaining) { @@ -5808,6 +6009,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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(); @@ -5835,6 +6037,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * 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) @@ -5854,6 +6057,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, final AlignFrame us = this; chooser.setResponseHandler(0, new Runnable() { + @Override public void run() { @@ -5868,6 +6072,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } private Rectangle lastFeatureSettingsBounds = null; + @Override public void setFeatureSettingsGeometry(Rectangle bounds) { @@ -5904,12 +6109,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * true is visible * @return list */ + public String[] getFeatureGroupsOfState(boolean visible) { jalview.api.FeatureRenderer fr = null; if (alignPanel != null - && (fr = alignPanel - .getFeatureRenderer()) != null) + && (fr = alignPanel.getFeatureRenderer()) != null) { List gps = fr.getGroups(visible); String[] _gps = gps.toArray(new String[gps.size()]); @@ -5922,6 +6127,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, * * @return list of feature groups on the view */ + public String[] getFeatureGroups() { jalview.api.FeatureRenderer fr = null; @@ -5941,43 +6147,47 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, alignPanel.getSeqPanel().selection(sel, csel, hidden, null); } - -} - -class PrintThread extends Thread -{ - AlignmentPanel ap; - - public PrintThread(AlignmentPanel ap) + public int getID() { - this.ap = ap; + return id; } - static PageFormat pf; - - @Override - public void run() + static class PrintThread extends Thread { - PrinterJob printJob = PrinterJob.getPrinterJob(); + AlignmentPanel ap; - if (pf != null) + public PrintThread(AlignmentPanel ap) { - printJob.setPrintable(ap, pf); - } - else - { - 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.setPrintable(ap, pf); + } + else { - printJob.print(); - } catch (Exception PrintException) + printJob.setPrintable(ap); + } + + if (printJob.printDialog()) { - PrintException.printStackTrace(); + try + { + printJob.print(); + } catch (Exception PrintException) + { + PrintException.printStackTrace(); + } } } } } + diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 1849447..7ab84ad 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -223,7 +223,7 @@ f */ 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)); @@ -394,7 +394,7 @@ f */ if (align != null) { StructureSelectionManager ssm = StructureSelectionManager - .getStructureSelectionManager(Desktop.instance); + .getStructureSelectionManager(Desktop.getInstance()); ssm.registerMappings(align.getCodonFrames()); } @@ -416,7 +416,7 @@ f */ if (mappings != null) { StructureSelectionManager ssm = StructureSelectionManager - .getStructureSelectionManager(Desktop.instance); + .getStructureSelectionManager(Desktop.getInstance()); for (AlignedCodonFrame acf : mappings) { if (noReferencesTo(acf)) @@ -542,7 +542,7 @@ f */ public void sendSelection() { jalview.structure.StructureSelectionManager - .getStructureSelectionManager(Desktop.instance) + .getStructureSelectionManager(Desktop.getInstance()) .sendSelection(new SequenceGroup(getSelectionGroup()), new ColumnSelection(getColumnSelection()), new HiddenColumns(getAlignment().getHiddenColumns()), @@ -588,7 +588,7 @@ f */ public StructureSelectionManager getStructureSelectionManager() { return StructureSelectionManager - .getStructureSelectionManager(Desktop.instance); + .getStructureSelectionManager(Desktop.getInstance()); } @Override @@ -754,7 +754,8 @@ f */ } ranges.setEndSeq(getAlignment().getHeight() - 1); // BH 2019.04.18 - firePropertyChange("alignment", null, getAlignment().getSequences()); + notifyAlignment(); + } /** @@ -780,7 +781,7 @@ f */ * dialog responses 0, 1, 2 (even though JOptionPane shows them * in reverse order) */ - JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop) + JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane()) .setResponseHandler(NO_SPLIT, new Runnable() { @Override @@ -1147,4 +1148,5 @@ f */ { this.viewName = viewName; } + } diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index eb612c8..b57bc08 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -20,29 +20,6 @@ */ 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,7 @@ public class AlignmentPanel extends GAlignmentPanel implements */ public AlignmentPanel(AlignFrame af, final AlignViewport av) { -// setBackground(Color.white); // BH 2019 + // setBackground(Color.white); // BH 2019 alignFrame = af; this.av = av; setSeqPanel(new SeqPanel(av, this)); @@ -173,6 +174,7 @@ public class AlignmentPanel extends GAlignmentPanel implements ranges.setViewportWidth(widthInRes); ranges.setViewportHeight(heightInSeq); } + repaint(); } }); @@ -183,10 +185,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 +268,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 +310,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 +320,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 +486,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 +568,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 +593,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(); @@ -587,10 +607,17 @@ public class AlignmentPanel extends GAlignmentPanel implements */ public void updateLayout() { + // BH 2020.06.09 avoiding negative values for SequencePanel and SeqCanvas + // dimensions. + + if (getTopLevelAncestor() == null) + return; + + + 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); @@ -630,7 +657,14 @@ public class AlignmentPanel extends GAlignmentPanel implements idSpaceFillerPanel1.setVisible(!wrap); - repaint(); + // 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()); + + // BH not added to anything yet! repaint(); } /** @@ -642,10 +676,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 +692,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 +726,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 +852,6 @@ public class AlignmentPanel extends GAlignmentPanel implements av.isShowAutocalculatedAbove()); sorter.sort(getAlignment().getAlignmentAnnotation(), av.getSortAnnotationsBy()); - repaint(); if (updateStructures) { @@ -837,11 +859,14 @@ public class AlignmentPanel extends GAlignmentPanel implements } if (updateOverview) { + alignFrame.repaint(); if (overviewPanel != null) { overviewPanel.updateOverviewImage(); } + } else { + repaint(); } } @@ -861,7 +886,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 +1045,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 +1090,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 +1142,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 +1308,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 +1331,8 @@ public class AlignmentPanel extends GAlignmentPanel implements text.append(" features = seq.findFeatures(column, column); + List features = seq.findFeatures(column, + column); for (SequenceFeature sf : features) { if (sf.isContactFeature()) @@ -1702,10 +1728,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 +1740,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. @@ -1854,4 +1883,84 @@ public class AlignmentPanel extends GAlignmentPanel implements 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.setSelectionGroup(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 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()); + } + } diff --git a/src/jalview/gui/AnnotationChooser.java b/src/jalview/gui/AnnotationChooser.java index 791421d..233f280 100644 --- a/src/jalview/gui/AnnotationChooser.java +++ b/src/jalview/gui/AnnotationChooser.java @@ -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) diff --git a/src/jalview/gui/AnnotationColourChooser.java b/src/jalview/gui/AnnotationColourChooser.java index e89c1c2..bd05be1 100644 --- a/src/jalview/gui/AnnotationColourChooser.java +++ b/src/jalview/gui/AnnotationColourChooser.java @@ -301,7 +301,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter updateView(); } }; - JalviewColourChooser.showColourChooser(Desktop.getDesktop(), ttl, + JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(), ttl, colourPanel.getBackground(), listener); } diff --git a/src/jalview/gui/AnnotationExporter.java b/src/jalview/gui/AnnotationExporter.java index d84287f..541c63a 100644 --- a/src/jalview/gui/AnnotationExporter.java +++ b/src/jalview/gui/AnnotationExporter.java @@ -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); } /** diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index 5a681f1..c843f37 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -20,21 +20,6 @@ */ 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 java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; @@ -62,6 +47,21 @@ 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; + /** * The panel that holds the labels for alignment annotations, providing * tooltips, context menus, drag to reorder rows, and drag to adjust panel @@ -971,7 +971,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; @@ -981,20 +981,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) { diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index 35ae242..01be3b5 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -1009,23 +1009,28 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, g.setColor(Color.white); g.fillRect(0, 0, visibleRect.width, visibleRect.height); + ViewportRanges ranges = av.getRanges(); + + + if (image != null) { // 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); fastPaint = false; return; } } - imgWidth = (av.getRanges().getEndRes() - av.getRanges().getStartRes() + + imgWidth = (ranges.getEndRes() - ranges.getStartRes() + 1) * av.getCharWidth(); + if (imgWidth < 1) { + fastPaint = false; return; } Graphics2D gg; @@ -1068,8 +1073,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, gg = (Graphics2D) image.getGraphics(); } - - drawComponent(gg, av.getRanges().getStartRes(), + + drawComponent(gg, ranges.getStartRes(), av.getRanges().getEndRes() + 1); gg.dispose(); imageFresh = false; @@ -1077,11 +1082,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } /** - * set true to enable redraw timing debug output on stderr - */ - private final boolean debugRedraw = false; - - /** * non-Thread safe repaint * * @param horizontal @@ -1101,6 +1101,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, int sr = av.getRanges().getStartRes(); int er = av.getRanges().getEndRes() + 1; int transX = 0; + + if (er == sr + 1) { + fastPaint = false; + return; + } Graphics2D gg = (Graphics2D) image.getGraphics(); diff --git a/src/jalview/gui/AppJmol.java b/src/jalview/gui/AppJmol.java index e13df4a..afb727f 100644 --- a/src/jalview/gui/AppJmol.java +++ b/src/jalview/gui/AppJmol.java @@ -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() }), diff --git a/src/jalview/gui/AssociatePdbFileWithSeq.java b/src/jalview/gui/AssociatePdbFileWithSeq.java index fe0aedf..5b5bd18 100644 --- a/src/jalview/gui/AssociatePdbFileWithSeq.java +++ b/src/jalview/gui/AssociatePdbFileWithSeq.java @@ -20,18 +20,15 @@ */ 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; -import javax.swing.JOptionPane; - /** - * GUI related routines for associating PDB files with sequences + * GUI related routines for associating PDB files with sequences. A single + * static method. * * @author JimP * @@ -39,58 +36,56 @@ import javax.swing.JOptionPane; 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 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 + 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) { - 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; } } diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index 5248306..c69d0a8 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -319,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(); /* diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index c6d6e97..317eff5 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -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() }), diff --git a/src/jalview/gui/ColourMenuHelper.java b/src/jalview/gui/ColourMenuHelper.java index 43d7015..e38b55b 100644 --- a/src/jalview/gui/ColourMenuHelper.java +++ b/src/jalview/gui/ColourMenuHelper.java @@ -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 diff --git a/src/jalview/gui/CrossRefAction.java b/src/jalview/gui/CrossRefAction.java index 2ada4d2..cc25696 100644 --- a/src/jalview/gui/CrossRefAction.java +++ b/src/jalview/gui/CrossRefAction.java @@ -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 diff --git a/src/jalview/gui/CutAndPasteTransfer.java b/src/jalview/gui/CutAndPasteTransfer.java index d328a0d..4badcba 100644 --- a/src/jalview/gui/CutAndPasteTransfer.java +++ b/src/jalview/gui/CutAndPasteTransfer.java @@ -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); diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 0e2fcf0..3d91e47 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -94,6 +94,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; @@ -108,6 +111,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; @@ -130,9 +134,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 = "

        Development managed by The Barton Group, University of Dundee, Scotland, UK.
        " + "

        For help, see the FAQ at www.jalview.org/faq and/or join the jalview-discuss@jalview.org mailing list" @@ -201,41 +207,47 @@ public class Desktop extends jalview.jbgui.GDesktop listener); } - /** Singleton Desktop instance */ - public static Desktop instance; + private MyDesktopPane desktopPane; + + public static MyDesktopPane getDesktopPane() + { + Desktop desktop = getInstance(); + return desktop == null ? null : desktop.desktopPane; + } /** - * BH TEMPORARY ONLY -- should use ApplicationSingleton + * 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() { - return instance; + return Jalview.isHeadlessMode() ? null + : (Desktop) ApplicationSingletonProvider + .getInstance(Desktop.class); } - public static MyDesktopPane desktop; - - public static MyDesktopPane getDesktop() + public static StructureSelectionManager getStructureSelectionManager() { - // BH 2018 could use currentThread() here as a reference to a - // Hashtable in JavaScript - return desktop; + return StructureSelectionManager + .getStructureSelectionManager(getInstance()); } - static int openFrameCount = 0; + 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 { @@ -256,7 +268,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); } } @@ -304,14 +316,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 @@ -361,190 +373,202 @@ 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); - } - }); - } + checkURLLinks(); - this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this)); + // Spawn a thread that shows the splashscreen - this.addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent evt) - { - quit(); - } - }); + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + new SplashScreen(true); + } + }); - MouseAdapter ma; - this.addMouseListener(ma = new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent evt) - { - if (evt.isPopupTrigger()) // Mac - { - showPasteMenu(evt.getX(), evt.getY()); + // 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); + } + }); } - } - @Override - public void mouseReleased(MouseEvent evt) - { - if (evt.isPopupTrigger()) // Windows + this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this)); + + this.addWindowListener(new WindowAdapter() { - showPasteMenu(evt.getX(), evt.getY()); - } + @Override + public void windowClosing(WindowEvent evt) + { + quit(); + } + }); + + MouseAdapter ma; + this.addMouseListener(ma = new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent evt) + { + if (evt.isPopupTrigger()) // Mac + { + showPasteMenu(evt.getX(), evt.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent evt) + { + if (evt.isPopupTrigger()) // Windows + { + showPasteMenu(evt.getX(), evt.getY()); + } + } + }); + desktopPane.addMouseListener(ma); } - }); - desktop.addMouseListener(ma); + } catch (Throwable t) + { + t.printStackTrace(); + } } @@ -568,10 +592,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)); } @@ -620,7 +644,7 @@ public class Desktop extends jalview.jbgui.GDesktop } } }).start(); - + } @Override @@ -641,10 +665,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(); @@ -665,10 +689,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), @@ -678,10 +700,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); @@ -752,69 +774,81 @@ 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 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 + * 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 * @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 and make it visible - * - * @param frame - * Frame to show - * @param title - * Visible Title - * @param w - * 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 + getInstance().addFrame(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 @@ -836,7 +870,29 @@ public class Desktop extends jalview.jbgui.GDesktop final JInternalFrame frame, String title, boolean makeVisible, int w, int h, boolean resizable, boolean ignoreMinSize) { + // 15 classes + getInstance().addFrame(frame, title, makeVisible, w, h, resizable, + ignoreMinSize); + } + + // 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, String title, + boolean makeVisible, int w, int h, boolean resizable, + boolean ignoreMinSize) + { // 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 @@ -851,19 +907,20 @@ public class Desktop extends jalview.jbgui.GDesktop // 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"))) + if (Jalview.isHeadlessMode()) { return; } 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, @@ -872,6 +929,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)); + } } @@ -881,7 +942,6 @@ public class Desktop extends jalview.jbgui.GDesktop frame.setMaximizable(resizable); frame.setIconifiable(resizable); frame.setOpaque(Platform.isJS()); - boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null); if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1) { frame.setLocation(xOffset * openFrameCount, @@ -898,7 +958,7 @@ public class Desktop extends jalview.jbgui.GDesktop @Override public void internalFrameActivated(InternalFrameEvent evt) { - JInternalFrame itf = desktop.getSelectedFrame(); + JInternalFrame itf = getDesktopPane().getSelectedFrame(); if (itf != null) { if (itf instanceof AlignFrame) @@ -930,7 +990,7 @@ public class Desktop extends jalview.jbgui.GDesktop { menuItem.removeActionListener(menuItem.getActionListeners()[0]); } - windowMenu.remove(menuItem); + getInstance().windowMenu.remove(menuItem); } }); @@ -952,9 +1012,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 @@ -972,14 +1032,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 @@ -1012,7 +1071,7 @@ public class Desktop extends jalview.jbgui.GDesktop { if (!internalCopy) { - Desktop.jalviewClipboard = null; + jalviewClipboard = null; } internalCopy = false; @@ -1057,7 +1116,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(); @@ -1113,8 +1172,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( @@ -1247,7 +1307,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); @@ -1268,7 +1328,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, @@ -1288,9 +1348,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); } /* @@ -1300,10 +1361,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())); @@ -1334,9 +1393,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 + ""); @@ -1447,7 +1506,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 @@ -1514,7 +1573,7 @@ public class Desktop extends jalview.jbgui.GDesktop @Override protected void showMemusage_actionPerformed(ActionEvent e) { - desktop.showMemoryUsage(showMemusage.isSelected()); + desktopPane.showMemoryUsage(showMemusage.isSelected()); } /* @@ -1551,7 +1610,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; @@ -1691,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) { @@ -1726,7 +1786,7 @@ public class Desktop extends jalview.jbgui.GDesktop setProgressBar(null, chosenFile.hashCode()); } }).start(); - } + } } @Override @@ -1757,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() @@ -1775,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); } @@ -1828,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(); @@ -1841,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; } @@ -1895,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 @@ -1937,9 +2000,9 @@ public class Desktop extends jalview.jbgui.GDesktop public static AlignmentViewport[] getViewports(String sequenceSetId) { List viewp = new ArrayList<>(); - if (desktop != null) + if (getDesktopPane() != null) { - AlignFrame[] frames = Desktop.getAlignFrames(); + AlignFrame[] frames = getAlignFrames(); for (AlignFrame afr : frames) { @@ -1986,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++) { @@ -2021,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() @@ -2039,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 */ @@ -2049,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++) { @@ -2093,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(); } @@ -2102,7 +2163,7 @@ public class Desktop extends jalview.jbgui.GDesktop public JInternalFrame[] getAllFrames() { - return desktop.getAllFrames(); + return desktopPane.getAllFrames(); } /** @@ -2188,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); @@ -2199,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; @@ -2303,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) { @@ -2352,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) { @@ -2388,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"), @@ -2441,14 +2501,23 @@ 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() { +<<<<<<< HEAD + getRootPane() + .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + KeyStroke + .getKeyStroke(KeyEvent.VK_Q, + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx()), +======= getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Platform.SHORTCUT_KEY_MASK), +>>>>>>> refs/heads/Jalview-JS/develop.JAL-3446.ctrlDown "Quit"); getRootPane().getActionMap().put("Quit", new AbstractAction() { @@ -2577,15 +2646,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()); @@ -2647,7 +2717,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); } @@ -2658,7 +2728,7 @@ public class Desktop extends jalview.jbgui.GDesktop if (Cache.getDefault("SHOW_JWS2_SERVICES", true)) { - t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer() + t2 = jalview.ws.jws2.Jws2Discoverer.getInstance() .startDiscoverer(changeSupport); } Thread t3 = null; @@ -2691,7 +2761,7 @@ public class Desktop extends jalview.jbgui.GDesktop { if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector) { - final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer() + final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance() .getErrorMessages(); if (ermsg != null) { @@ -2730,7 +2800,7 @@ public class Desktop extends jalview.jbgui.GDesktop * * jd.waitForInput(); */ - JvOptionPane.showConfirmDialog(Desktop.desktop, + JvOptionPane.showConfirmDialog(desktopPane, new JLabel("
        " + ermsg + "
        " + "

        It may be that you have invalid JABA URLs
        in your web service preferences," @@ -2767,7 +2837,7 @@ public class Desktop extends jalview.jbgui.GDesktop */ public static void showUrl(final String url) { - showUrl(url, Desktop.instance); + showUrl(url, getInstance()); } /** @@ -2796,7 +2866,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"), @@ -2836,7 +2906,7 @@ public class Desktop extends jalview.jbgui.GDesktop try { url = e.getURL().toString(); - Desktop.showUrl(url); + showUrl(url); } catch (Exception x) { if (url != null) @@ -2895,7 +2965,7 @@ public class Desktop extends jalview.jbgui.GDesktop { } } - if (instance == null) + if (Jalview.isHeadlessMode()) { return; } @@ -2955,8 +3025,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. * @@ -3021,7 +3091,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); } /* @@ -3059,7 +3129,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) @@ -3122,12 +3192,14 @@ public class Desktop extends jalview.jbgui.GDesktop * - the payload from the drop event * @throws Exception */ + @SuppressWarnings("unchecked") public static void transferFromDropTarget(List files, List protocols, DropTargetDropEvent evt, Transferable t) throws Exception { - // BH 2018 changed List to List to allow for File from SwingJS + // BH 2018 changed List to List to allow for File from + // SwingJS // DataFlavor[] flavors = t.getTransferDataFlavors(); // for (int i = 0; i < flavors.length; i++) { @@ -3139,7 +3211,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); @@ -3191,7 +3264,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) t .getTransferData(DataFlavor.javaFileListFlavor)) { files.add(file); @@ -3339,10 +3412,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 @@ -3355,7 +3428,7 @@ public class Desktop extends jalview.jbgui.GDesktop Class structureViewerClass) { List result = new ArrayList<>(); - JInternalFrame[] frames = Desktop.instance.getAllFrames(); + JInternalFrame[] frames = getAllFrames(); for (JInternalFrame frame : frames) { diff --git a/src/jalview/gui/FeatureEditor.java b/src/jalview/gui/FeatureEditor.java index d547c58..a02ec36 100644 --- a/src/jalview/gui/FeatureEditor.java +++ b/src/jalview/gui/FeatureEditor.java @@ -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) { diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index b49593a..8c4b355 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -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); } diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java index a1693f7..01c8a16 100755 --- a/src/jalview/gui/Finder.java +++ b/src/jalview/gui/Finder.java @@ -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]; diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java index 92cc4c6..2d9682b 100755 --- a/src/jalview/gui/FontChooser.java +++ b/src/jalview/gui/FontChooser.java @@ -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); diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index 10c0787..14ae033 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -20,6 +20,10 @@ */ 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 searchResults; @@ -137,6 +137,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,6 +222,11 @@ 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()); @@ -244,7 +250,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 +390,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 +603,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() + { + fastPaint = false; + } } diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index 4b5e9d4..2734b8d 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -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); diff --git a/src/jalview/gui/JalviewDialog.java b/src/jalview/gui/JalviewDialog.java index 1d7bf3d..0ff5606 100644 --- a/src/jalview/gui/JalviewDialog.java +++ b/src/jalview/gui/JalviewDialog.java @@ -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)); } diff --git a/src/jalview/gui/JvSwingUtils.java b/src/jalview/gui/JvSwingUtils.java index 2f4a0fe..f23dcf8 100644 --- a/src/jalview/gui/JvSwingUtils.java +++ b/src/jalview/gui/JvSwingUtils.java @@ -47,6 +47,7 @@ import javax.swing.border.Border; import javax.swing.border.TitledBorder; import jalview.util.MessageManager; +import jalview.util.Platform; /** * useful functions for building Swing GUIs @@ -56,12 +57,18 @@ import jalview.util.MessageManager; */ public final class JvSwingUtils { + static final String HTML_PREFIX = (Platform.isJS() ? + "
        " + : "
        " + ); + /** * 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 <html> wrapper tags + * if true, add <html> wrapper tags (currently false for only + * two references -- both in Jws2Discoverer -- * @param ttext * * @return @@ -70,40 +77,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("
        ", "
        "); - if (ttext.contains("
        ")) + boolean maxLengthExceeded = false; + boolean isHTML = ttext.startsWith(""); + if (isHTML) { - String[] htmllines = ttext.split("
        "); - for (String line : htmllines) - { - maxLengthExceeded = line.length() > 60; - if (maxLengthExceeded) - { + ttext = ttext.substring(6); + } + if (ttext.endsWith("")) + { + isHTML = true; + ttext = ttext.substring(0, ttext.length() - 7); + } + boolean hasBR = ttext.contains("
        "); + enclose |= isHTML || hasBR; + if (hasBR) + { + int pt = -1, ptlast = -4; + while ((pt = ttext.indexOf("
        ", pt + 1)) >= 0) { + if (pt - ptlast - 4 > 60) { + maxLengthExceeded = true; break; } } } - else + else { maxLengthExceeded = ttext.length() > 60; } - if (!maxLengthExceeded) - { - return enclose ? "" + ttext + "" : ttext; - } - - return (enclose ? "" : "") - // BH 2018 - + "
        " -// + "

        " - + ttext - + "

        " -// + "

        " - + ((enclose ? "" : "")); + String ret = (!enclose ? ttext : maxLengthExceeded ? HTML_PREFIX + ttext + "
        " : + "" + ttext + ""); + //System.out.println("JvSwUtil " + enclose + " " + maxLengthExceeded + " " + ret); + return ret; } public static JButton makeButton(String label, String tooltip, diff --git a/src/jalview/gui/LineartOptions.java b/src/jalview/gui/LineartOptions.java index 9704b39..62bdd35 100644 --- a/src/jalview/gui/LineartOptions.java +++ b/src/jalview/gui/LineartOptions.java @@ -86,7 +86,7 @@ public class LineartOptions extends JPanel ex.printStackTrace(); } - dialog = JvOptionPane.newOptionDialog(Desktop.desktop); + dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane()); } /** diff --git a/src/jalview/gui/OOMWarning.java b/src/jalview/gui/OOMWarning.java index 02c8fe1..dc5d0f5 100644 --- a/src/jalview/gui/OOMWarning.java +++ b/src/jalview/gui/OOMWarning.java @@ -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 diff --git a/src/jalview/gui/PaintRefresher.java b/src/jalview/gui/PaintRefresher.java index 953fdc5..326bac0 100755 --- a/src/jalview/gui/PaintRefresher.java +++ b/src/jalview/gui/PaintRefresher.java @@ -128,13 +128,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(); } diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 2a7fb9f..4469b43 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -301,7 +301,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); @@ -1699,7 +1699,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener menuItem.setEnabled(true); for (String calcId : tipEntries.keySet()) { - tooltip.append("
        " + calcId + "/" + tipEntries.get(calcId)); + tooltip.append("
        " + calcId + "/" + tipEntries.get(calcId)); } String tooltipText = JvSwingUtils.wrapTooltip(true, tooltip.toString()); @@ -2040,8 +2040,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(); } }); } @@ -2083,7 +2082,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener refresh(); } }; - JalviewColourChooser.showColourChooser(Desktop.getDesktop(), + JalviewColourChooser.showColourChooser(Desktop.getDesktopPane(), title, Color.BLUE, listener); } @@ -2172,9 +2171,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()); } } @@ -2281,8 +2279,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(); } }); } diff --git a/src/jalview/gui/Preferences.java b/src/jalview/gui/Preferences.java index 55345e0..4030afa 100755 --- a/src/jalview/gui/Preferences.java +++ b/src/jalview/gui/Preferences.java @@ -781,16 +781,16 @@ public class Preferences extends GPreferences protColour.getSelectedItem().toString()); 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 Overview settings */ - Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground()); - Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground()); + Cache.setColourPropertyNoSave(GAP_COLOUR, gapColour.getBackground()); + Cache.setColourPropertyNoSave(HIDDEN_COLOUR, hiddenColour.getBackground()); Cache.setPropertyNoSave(USE_LEGACY_GAP, Boolean.toString(useLegacyGap.isSelected())); Cache.setPropertyNoSave(SHOW_OV_HIDDEN_AT_START, @@ -807,8 +807,11 @@ public class Preferences extends GPreferences Boolean.toString(useRnaView.isSelected())); Cache.setPropertyNoSave(STRUCT_FROM_PDB, Boolean.toString(structFromPdb.isSelected())); - Cache.setPropertyNoSave(STRUCTURE_DISPLAY, - structViewer.getSelectedItem().toString()); + if (!Platform.isJS()) + { + Cache.setPropertyNoSave(STRUCTURE_DISPLAY, + structViewer.getSelectedItem().toString()); + } Cache.setOrRemove(CHIMERA_PATH, chimeraPath.getText()); Cache.setPropertyNoSave("MAP_WITH_SIFTS", Boolean.toString(siftsMapping.isSelected())); @@ -960,7 +963,7 @@ public class Preferences extends GPreferences BackupFilesPresetEntry.SAVEDCONFIG, savedBFPE.toString()); Cache.saveProperties(); - Desktop.instance.doConfigureStructurePrefs(); + Desktop.getInstance().doConfigureStructurePrefs(); try { frame.setClosed(true); @@ -1107,7 +1110,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) @@ -1159,7 +1162,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) @@ -1332,7 +1335,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"), @@ -1358,7 +1361,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); @@ -1397,7 +1400,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, diff --git a/src/jalview/gui/PromptUserConfig.java b/src/jalview/gui/PromptUserConfig.java index cb59452..77d83a8 100644 --- a/src/jalview/gui/PromptUserConfig.java +++ b/src/jalview/gui/PromptUserConfig.java @@ -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, diff --git a/src/jalview/gui/RedundancyPanel.java b/src/jalview/gui/RedundancyPanel.java index 6ed3248..c7e2b8e 100755 --- a/src/jalview/gui/RedundancyPanel.java +++ b/src/jalview/gui/RedundancyPanel.java @@ -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(); } diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index a6b4b49..fda1e03 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -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 * diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index b27208a..f62507a 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -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,12 @@ public class SeqCanvas extends JPanel implements ViewportListenerI private int wrappedVisibleWidths; // number of wrapped widths displayed + private int availWidth; + + private int availHeight; + // Don't do this! Graphics handles are supposed to be transient - //private Graphics2D gg; + // private Graphics2D gg; /** * Creates a new SeqCanvas object. @@ -116,7 +121,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI public SequenceRenderer getSequenceRenderer() { - return seqRdr; + return seqRdr; } public FeatureRenderer getFeatureRenderer() @@ -202,7 +207,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 +243,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI } } - /* * white fill the space for the scale */ @@ -343,7 +347,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI } } - // System.err.println(">>> FastPaint to " + transX + " " + transY + " " // + horizontal + " " + vertical + " " + startRes + " " + endRes // + " " + startSeq + " " + endSeq); @@ -352,9 +355,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); @@ -374,19 +374,14 @@ 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; } @@ -436,10 +431,11 @@ public class SeqCanvas extends JPanel implements ViewportListenerI // 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 +448,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 +470,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI drawCursor(g, startRes, endRes, startSeq, endSeq); } } - + /** * Draw an alignment panel for printing * @@ -494,8 +490,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 +514,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 +569,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 +584,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 +615,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 +626,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 +646,26 @@ public class SeqCanvas extends JPanel implements ViewportListenerI *
      1. whether scales are shown left, right or above the alignment
      2. * * + * @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 +679,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 +751,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 +818,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 +830,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 +899,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 +921,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 +949,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 +1227,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 +1298,6 @@ public class SeqCanvas extends JPanel implements ViewportListenerI } } - /** * Draw a selection group over an unwrapped alignment * @@ -1285,7 +1320,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 +1343,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 +1513,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 +1529,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 +1687,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 +1706,161 @@ 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]; + + // System.out.println("SC dx dy " + scrollX + " " + scrollY); + + 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 +1897,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 +1915,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 +1929,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI drawWrappedDecorators(gg, ranges.getStartRes()); gg.dispose(); - + repaint(); } finally { @@ -1849,7 +1954,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 +1965,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 +1982,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 +2035,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 +2123,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 +2134,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 +2154,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI boolean matchFound = false; - calculateWrappedGeometry(getWidth(), getHeight()); + calculateWrappedGeometry(); int wrappedWidth = av.getWrappedWidth(); int wrappedHeight = wrappedRepeatHeightPx; @@ -2063,8 +2167,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 +2181,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 +2224,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 +2252,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI } } } - + gg.dispose(); return matchFound; @@ -2165,4 +2268,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() + { + fastPaint = false; + } + } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index b2149df..3de631f 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -291,8 +291,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 @@ -424,8 +423,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 { diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java index 8b5d3b7..3d09f99 100755 --- a/src/jalview/gui/SequenceFetcher.java +++ b/src/jalview/gui/SequenceFetcher.java @@ -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); } diff --git a/src/jalview/gui/SliderPanel.java b/src/jalview/gui/SliderPanel.java index 46b47a2..7effb0f 100755 --- a/src/jalview/gui/SliderPanel.java +++ b/src/jalview/gui/SliderPanel.java @@ -231,8 +231,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 @@ -307,8 +307,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() { diff --git a/src/jalview/gui/SplashScreen.java b/src/jalview/gui/SplashScreen.java index f4b275d..0edede4 100755 --- a/src/jalview/gui/SplashScreen.java +++ b/src/jalview/gui/SplashScreen.java @@ -160,7 +160,7 @@ public class SplashScreen extends JPanel System.err.println("Error when loading images!"); } } while (!mt.checkAll()); - Desktop.instance.setIconImage(logo); + Desktop.getInstance().setIconImage(logo); } } catch (Exception ex) { @@ -191,7 +191,7 @@ public class SplashScreen extends JPanel } add(splashText, BorderLayout.CENTER); splashText.addMouseListener(closer); - Desktop.desktop.add(iframe); + Desktop.getDesktopPane().add(iframe); refreshText(); } @@ -200,7 +200,7 @@ public class SplashScreen extends JPanel */ protected boolean refreshText() { - String newtext = Desktop.instance.getAboutMessage(); + String newtext = Desktop.getInstance().getAboutMessage(); // System.err.println("Text found: \n"+newtext+"\nEnd of newtext."); if (oldTextLength != newtext.length()) { @@ -239,8 +239,8 @@ public class SplashScreen extends JPanel splashText.setSize(new Dimension(750, 375)); add(splashText, BorderLayout.CENTER); revalidate(); - iframe.setBounds((Desktop.instance.getWidth() - 750) / 2, - (Desktop.instance.getHeight() - 375) / 2, 750, + iframe.setBounds((Desktop.getInstance().getWidth() - 750) / 2, + (Desktop.getInstance().getHeight() - 375) / 2, 750, splashText.getHeight() + iconimg.getHeight()); iframe.validate(); iframe.setVisible(true); @@ -286,7 +286,7 @@ public class SplashScreen extends JPanel } closeSplash(); - Desktop.instance.startDialogQueue(); + Desktop.getInstance().startDialogQueue(); } /** diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java index f57279e..953755f 100644 --- a/src/jalview/gui/SplitFrame.java +++ b/src/jalview/gui/SplitFrame.java @@ -152,7 +152,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(); @@ -170,7 +170,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()); } @@ -571,7 +571,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); } @@ -698,7 +698,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI */ protected void expandViews_actionPerformed() { - Desktop.instance.explodeViews(this); + Desktop.getInstance().explodeViews(this); } /** @@ -707,7 +707,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI */ protected void gatherViews_actionPerformed() { - Desktop.instance.gatherViews(this); + Desktop.getInstance().gatherViews(this); } /** diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index 4a8a7d8..c30d8c7 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -145,7 +145,7 @@ public class StructureChooser extends GStructureChooser */ private void discoverStructureViews() { - if (Desktop.instance != null) + if (Desktop.getInstance() != null) { targetView.removeAllItems(); if (lastTargetedView != null && !lastTargetedView.isVisible()) @@ -153,7 +153,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 @@ -886,11 +886,9 @@ 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(ssm); + final StructureViewer theViewer = getTargetedStructureViewer(); boolean superimpose = chk_superpose.isSelected(); Runnable viewStruc = new Runnable() @@ -929,9 +927,7 @@ 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; @@ -1005,10 +1001,8 @@ public class StructureChooser extends GStructureChooser { selectedSequence = userSelectedSeq; } - PDBEntry fileEntry = new AssociatePdbFileWithSeq() - .associatePdbWithSeq(selectedPdbFileName, - DataSourceType.FILE, selectedSequence, true, - Desktop.instance); + PDBEntry fileEntry = AssociatePdbFileWithSeq.associatePdbWithSeq(selectedPdbFileName, + DataSourceType.FILE, selectedSequence, true); sViewer = StructureViewer.launchStructureViewer(ap, new PDBEntry[] { fileEntry }, new SequenceI[] { selectedSequence }, superimpose, theViewer, @@ -1066,11 +1060,9 @@ public class StructureChooser extends GStructureChooser * @param ssm * @return */ - StructureViewer getTargetedStructureViewer(StructureSelectionManager ssm) + StructureViewer getTargetedStructureViewer() { - Object sv = targetView.getSelectedItem(); - - return sv == null ? new StructureViewer(ssm) : (StructureViewer) sv; + return (StructureViewer) targetView.getSelectedItem(); } /** diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java index e0c33e5..e7a30f9 100644 --- a/src/jalview/gui/StructureViewer.java +++ b/src/jalview/gui/StructureViewer.java @@ -433,6 +433,8 @@ public class StructureViewer 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( diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index 418a84d..af48093 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -407,7 +407,7 @@ public abstract class StructureViewerBase extends GStructureViewer */ protected List getViewersFor(AlignmentPanel alp) { - return Desktop.instance.getStructureViewers(alp, this.getClass()); + return Desktop.getInstance().getStructureViewers(alp, this.getClass()); } @Override diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java index 29ba52b..e59dfcc 100755 --- a/src/jalview/gui/TreeCanvas.java +++ b/src/jalview/gui/TreeCanvas.java @@ -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 } } diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index 37b4dac..d242892 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -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) evt.getNewValue()); treeCanvas.nameHash.clear(); // reset the mapping between canvas // rectangles and leafnodes repaint(); + break; } } }; diff --git a/src/jalview/gui/UserDefinedColours.java b/src/jalview/gui/UserDefinedColours.java index 4846049..1139ae0 100755 --- a/src/jalview/gui/UserDefinedColours.java +++ b/src/jalview/gui/UserDefinedColours.java @@ -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 }), diff --git a/src/jalview/gui/UserQuestionnaireCheck.java b/src/jalview/gui/UserQuestionnaireCheck.java index ef86756..e12586a 100644 --- a/src/jalview/gui/UserQuestionnaireCheck.java +++ b/src/jalview/gui/UserQuestionnaireCheck.java @@ -141,7 +141,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); diff --git a/src/jalview/gui/VamsasApplication.java b/src/jalview/gui/VamsasApplication.java index 0848a4d..2ee3e06 100644 --- a/src/jalview/gui/VamsasApplication.java +++ b/src/jalview/gui/VamsasApplication.java @@ -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() { diff --git a/src/jalview/gui/WebserviceInfo.java b/src/jalview/gui/WebserviceInfo.java index 25ade21..ab9444e 100644 --- a/src/jalview/gui/WebserviceInfo.java +++ b/src/jalview/gui/WebserviceInfo.java @@ -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); } diff --git a/src/jalview/gui/WsJobParameters.java b/src/jalview/gui/WsJobParameters.java index 976e551..45822a3 100644 --- a/src/jalview/gui/WsJobParameters.java +++ b/src/jalview/gui/WsJobParameters.java @@ -215,12 +215,12 @@ public class WsJobParameters extends JPanel implements ItemListener, public boolean showRunDialog() { - frame = new JDialog(Desktop.instance, true); + frame = new JDialog(Desktop.getInstance(), true); frame.setTitle(MessageManager.formatMessage("label.edit_params_for", new String[] { service.getActionText() })); - Rectangle deskr = Desktop.instance.getBounds(); + Rectangle deskr = Desktop.getInstance().getBounds(); Dimension pref = this.getPreferredSize(); frame.setBounds( new Rectangle((int) (deskr.getCenterX() - pref.width / 2), @@ -436,8 +436,8 @@ 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 ? 540 - : Desktop.instance.getHeight(); + final int windowHeight = Desktop.getInstance() == null ? 540 + : Desktop.getInstance().getHeight(); setPreferredSize(new Dimension(540, windowHeight)); add(dialogpanel, BorderLayout.SOUTH); validate(); @@ -963,13 +963,13 @@ public class WsJobParameters extends JPanel implements ItemListener, 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 services = new Vector<>(); services.addElement(args[p++]); - Jws2Discoverer.getDiscoverer().setServiceUrls(services); + Jws2Discoverer.getInstance().setServiceUrls(services); } try { diff --git a/src/jalview/gui/WsParamSetManager.java b/src/jalview/gui/WsParamSetManager.java index bb5d996..0f315eb 100644 --- a/src/jalview/gui/WsParamSetManager.java +++ b/src/jalview/gui/WsParamSetManager.java @@ -203,7 +203,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(); @@ -311,7 +311,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) { diff --git a/src/jalview/gui/WsPreferences.java b/src/jalview/gui/WsPreferences.java index 5186a26..b97a25e 100644 --- a/src/jalview/gui/WsPreferences.java +++ b/src/jalview/gui/WsPreferences.java @@ -65,7 +65,7 @@ public class WsPreferences extends GWsPreferences private void initFromPreferences() { - wsUrls = Jws2Discoverer.getDiscoverer().getServiceUrls(); + wsUrls = Jws2Discoverer.getInstance().getServiceUrls(); if (!wsUrls.isEmpty()) { oldUrls = new Vector(wsUrls); @@ -122,7 +122,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; } @@ -241,7 +241,7 @@ public class WsPreferences extends GWsPreferences private void updateServiceList() { - Jws2Discoverer.getDiscoverer().setServiceUrls(wsUrls); + Jws2Discoverer.getInstance().setServiceUrls(wsUrls); } private void updateRsbsServiceList() @@ -454,7 +454,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 @@ -472,13 +472,13 @@ 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); @@ -491,7 +491,7 @@ public class WsPreferences extends GWsPreferences } 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, @@ -502,7 +502,7 @@ public class WsPreferences extends GWsPreferences } else { - JvOptionPane.showInternalMessageDialog(Desktop.desktop, + JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), MessageManager.getString( "warn.server_didnt_pass_validation")); } @@ -595,7 +595,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(); @@ -616,15 +616,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(); @@ -646,8 +646,8 @@ public class WsPreferences extends GWsPreferences @Override protected void resetWs_actionPerformed(ActionEvent e) { - Jws2Discoverer.getDiscoverer().setServiceUrls(null); - List nwsUrls = Jws2Discoverer.getDiscoverer().getServiceUrls(); + Jws2Discoverer.getInstance().setServiceUrls(null); + List nwsUrls = Jws2Discoverer.getInstance().getServiceUrls(); if (!wsUrls.equals(nwsUrls)) { update++; diff --git a/src/jalview/httpserver/HttpServer.java b/src/jalview/httpserver/HttpServer.java index a18d38d..7021fae 100644 --- a/src/jalview/httpserver/HttpServer.java +++ b/src/jalview/httpserver/HttpServer.java @@ -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 myHandlers = new HashMap(); - - /* - * 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 myHandlers = new HashMap(); + + /* + * The context root for the server + */ + private URI contextRoot; + + /** * Start the http server * diff --git a/src/jalview/io/AppletFormatAdapter.java b/src/jalview/io/AppletFormatAdapter.java index 4916bb3..f01bfdc 100755 --- a/src/jalview/io/AppletFormatAdapter.java +++ b/src/jalview/io/AppletFormatAdapter.java @@ -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 index 0000000..69e29f8 --- /dev/null +++ b/src/jalview/io/BSMLFile.java @@ -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 . + * 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 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); + // + // + // + // + 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"; + } + +} diff --git a/src/jalview/io/BackupFiles.java b/src/jalview/io/BackupFiles.java index 0d5f92b..9537442 100644 --- a/src/jalview/io/BackupFiles.java +++ b/src/jalview/io/BackupFiles.java @@ -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"), diff --git a/src/jalview/io/BioJsHTMLOutput.java b/src/jalview/io/BioJsHTMLOutput.java index 9db3df2..5573e0d 100644 --- a/src/jalview/io/BioJsHTMLOutput.java +++ b/src/jalview/io/BioJsHTMLOutput.java @@ -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 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); diff --git a/src/jalview/io/FileFormats.java b/src/jalview/io/FileFormats.java index aadcdb9..fca29bd 100644 --- a/src/jalview/io/FileFormats.java +++ b/src/jalview/io/FileFormats.java @@ -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 formats; - - /* - * Formats in this set are capable of being identified by IdentifyFile - */ - private static Set 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 formats; + + /* + * Formats in this set are capable of being identified by IdentifyFile + */ + private Set 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 diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index ac55911..7eda110 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -275,9 +275,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) { @@ -299,12 +299,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(), @@ -315,7 +315,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 @@ -408,7 +408,7 @@ public class FileLoader implements Runnable // register PDB entries with desktop's structure selection // manager StructureSelectionManager - .getStructureSelectionManager(Desktop.instance) + .getStructureSelectionManager(Desktop.getInstance()) .registerPDBEntry(pdbe); } } @@ -501,23 +501,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"), @@ -545,7 +545,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 }), @@ -567,7 +567,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 }), @@ -589,7 +589,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) { @@ -611,9 +611,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(); } } diff --git a/src/jalview/io/SequenceAnnotationReport.java b/src/jalview/io/SequenceAnnotationReport.java index 8e4e783..4d0bec7 100644 --- a/src/jalview/io/SequenceAnnotationReport.java +++ b/src/jalview/io/SequenceAnnotationReport.java @@ -237,7 +237,7 @@ public class SequenceAnnotationReport { if (sb0.length() > 6) { - sb.append("
        "); + sb.append("
        "); } sb.append(feature.getType()).append(" ").append(begin).append(":") .append(end); @@ -247,7 +247,7 @@ public class SequenceAnnotationReport if (sb0.length() > 6) { - sb.append("
        "); + sb.append("
        "); } // TODO: remove this hack to display link only features boolean linkOnly = feature.getValue("linkonly") != null; @@ -276,7 +276,9 @@ public class SequenceAnnotationReport int linkindex = description.toLowerCase().indexOf(" -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; @@ -395,7 +397,7 @@ public class SequenceAnnotationReport { for (List urllink : createLinksFrom(null, urlstring)) { - sb.append("

        "); + + "

        "); } } catch (Exception x) { @@ -561,7 +563,7 @@ public class SequenceAnnotationReport countForSource++; if (countForSource == 1 || !summary) { - sb.append("
        "); + sb.append("
        "); } if (countForSource <= MAX_REFS_PER_SOURCE || !summary) { @@ -587,11 +589,11 @@ public class SequenceAnnotationReport } if (moreSources) { - sb.append("
        ").append(source).append(COMMA).append(ELLIPSIS); + sb.append("
        ").append(source).append(COMMA).append(ELLIPSIS); } if (ellipsis) { - sb.append("
        ("); + sb.append("
        ("); sb.append(MessageManager.getString("label.output_seq_details")); sb.append(")"); } diff --git a/src/jalview/io/VamsasAppDatastore.java b/src/jalview/io/VamsasAppDatastore.java index 387dbfa..c60fd88 100644 --- a/src/jalview/io/VamsasAppDatastore.java +++ b/src/jalview/io/VamsasAppDatastore.java @@ -720,9 +720,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++) @@ -1479,7 +1479,7 @@ public class VamsasAppDatastore if (mappings != null) { jalview.structure.StructureSelectionManager - .getStructureSelectionManager(Desktop.instance) + .getStructureSelectionManager(Desktop.getInstance()) .registerMappings(mappings); } } diff --git a/src/jalview/io/WSWUBlastClient.java b/src/jalview/io/WSWUBlastClient.java index 63b78b2..e24c2f3 100755 --- a/src/jalview/io/WSWUBlastClient.java +++ b/src/jalview/io/WSWUBlastClient.java @@ -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); diff --git a/src/jalview/io/cache/AppCache.java b/src/jalview/io/cache/AppCache.java index eaf6ecd..fdcad08 100644 --- a/src/jalview/io/cache/AppCache.java +++ b/src/jalview/io/cache/AppCache.java @@ -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>(); + } + 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> cacheItems; - private AppCache() - { - cacheItems = new Hashtable>(); - } /** * 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 diff --git a/src/jalview/io/gff/Gff3Helper.java b/src/jalview/io/gff/Gff3Helper.java index 1ef8848..e422ed4 100644 --- a/src/jalview/io/gff/Gff3Helper.java +++ b/src/jalview/io/gff/Gff3Helper.java @@ -91,7 +91,7 @@ public class Gff3Helper extends GffHelperBase String atts = gff[ATTRIBUTES_COL]; Map> 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)) { diff --git a/src/jalview/io/gff/InterProScanHelper.java b/src/jalview/io/gff/InterProScanHelper.java index 948cdd2..141b677 100644 --- a/src/jalview/io/gff/InterProScanHelper.java +++ b/src/jalview/io/gff/InterProScanHelper.java @@ -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]) diff --git a/src/jalview/io/gff/SequenceOntologyFactory.java b/src/jalview/io/gff/SequenceOntologyFactory.java index 90cae7a..08a2c6a 100644 --- a/src/jalview/io/gff/SequenceOntologyFactory.java +++ b/src/jalview/io/gff/SequenceOntologyFactory.java @@ -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; + } diff --git a/src/jalview/io/vamsas/Sequencemapping.java b/src/jalview/io/vamsas/Sequencemapping.java index 0a582e5..402ffdf 100644 --- a/src/jalview/io/vamsas/Sequencemapping.java +++ b/src/jalview/io/vamsas/Sequencemapping.java @@ -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); diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java index edaabf1..38ad437 100755 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@ -25,9 +25,10 @@ import jalview.analysis.GeneticCodeI; import jalview.analysis.GeneticCodes; import jalview.api.SplitContainerI; import jalview.bin.Cache; -import jalview.bin.Jalview; import jalview.gui.JvSwingUtils; +import jalview.gui.PaintRefresher; import jalview.gui.Preferences; +import jalview.gui.TreePanel; import jalview.io.FileFormats; import jalview.schemes.ResidueColourScheme; import jalview.util.MessageManager; @@ -35,6 +36,7 @@ import jalview.util.Platform; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Component; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -44,7 +46,9 @@ import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.swing.BorderFactory; @@ -222,7 +226,7 @@ public class GAlignFrame extends JInternalFrame { // for Web-page embedding using id=align-frame-div - setName(Jalview.getAppID("alignment")); + setName(Platform.getAppID("alignment")); jbInit(); @@ -1172,6 +1176,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(); } @@ -1712,8 +1734,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 @@ -1918,6 +1940,10 @@ public class GAlignFrame extends JInternalFrame // selectMenu.add(listenToViewSelections); } + protected void enableSortMenuOptions() + { + } + protected void loadVcf_actionPerformed() { } @@ -2716,4 +2742,6 @@ public class GAlignFrame extends JInternalFrame protected void showComplement_actionPerformed(boolean complement) { } + + } diff --git a/src/jalview/jbgui/GDesktop.java b/src/jalview/jbgui/GDesktop.java index 8e30f5a..0c179e8 100755 --- a/src/jalview/jbgui/GDesktop.java +++ b/src/jalview/jbgui/GDesktop.java @@ -21,7 +21,6 @@ package jalview.jbgui; import jalview.api.AlignmentViewPanel; -import jalview.bin.Jalview; import jalview.io.FileFormatException; import jalview.util.MessageManager; import jalview.util.Platform; @@ -46,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(); @@ -141,7 +140,7 @@ public class GDesktop extends JFrame private void jbInit() throws Exception { - setName(Jalview.getAppID("desktop")); + setName(Platform.getAppID("desktop")); FileMenu.setText(MessageManager.getString("action.file")); HelpMenu.setText(MessageManager.getString("action.help")); inputLocalFileMenuItem diff --git a/src/jalview/jbgui/GPCAPanel.java b/src/jalview/jbgui/GPCAPanel.java index 74b5afd..aa2f549 100755 --- a/src/jalview/jbgui/GPCAPanel.java +++ b/src/jalview/jbgui/GPCAPanel.java @@ -20,9 +20,9 @@ */ package jalview.jbgui; -import jalview.bin.Jalview; import jalview.util.ImageMaker.TYPE; import jalview.util.MessageManager; +import jalview.util.Platform; import java.awt.BorderLayout; import java.awt.Color; @@ -89,7 +89,7 @@ public class GPCAPanel extends JInternalFrame private void jbInit() throws Exception { - setName(Jalview.getAppID("pca")); + setName(Platform.getAppID("pca")); this.getContentPane().setLayout(new BorderLayout()); JPanel jPanel2 = new JPanel(); jPanel2.setLayout(new FlowLayout()); diff --git a/src/jalview/jbgui/GPreferences.java b/src/jalview/jbgui/GPreferences.java index 60f17ab..08fbea3 100755 --- a/src/jalview/jbgui/GPreferences.java +++ b/src/jalview/jbgui/GPreferences.java @@ -2259,7 +2259,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); diff --git a/src/jalview/jbgui/GSequenceLink.java b/src/jalview/jbgui/GSequenceLink.java index a43e504..2ec7051 100755 --- a/src/jalview/jbgui/GSequenceLink.java +++ b/src/jalview/jbgui/GSequenceLink.java @@ -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); diff --git a/src/jalview/jbgui/GStructureViewer.java b/src/jalview/jbgui/GStructureViewer.java index e16d63a..1e7cf37 100644 --- a/src/jalview/jbgui/GStructureViewer.java +++ b/src/jalview/jbgui/GStructureViewer.java @@ -21,10 +21,10 @@ package jalview.jbgui; import jalview.api.structures.JalviewStructureDisplayI; -import jalview.bin.Jalview; 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; @@ -90,7 +90,7 @@ public abstract class GStructureViewer extends JInternalFrame private void jbInit() throws Exception { - setName(Jalview.getAppID("structureviewer")); + setName(Platform.getAppID("structureviewer")); JMenuBar menuBar = new JMenuBar(); this.setJMenuBar(menuBar); diff --git a/src/jalview/jbgui/GTreePanel.java b/src/jalview/jbgui/GTreePanel.java index 0f9c2a2..3aff0e0 100755 --- a/src/jalview/jbgui/GTreePanel.java +++ b/src/jalview/jbgui/GTreePanel.java @@ -20,9 +20,9 @@ */ package jalview.jbgui; -import jalview.bin.Jalview; import jalview.util.ImageMaker.TYPE; import jalview.util.MessageManager; +import jalview.util.Platform; import java.awt.BorderLayout; import java.awt.Color; @@ -93,7 +93,7 @@ public class GTreePanel extends JInternalFrame private void jbInit() throws Exception { - setName(Jalview.getAppID("tree")); + setName(Platform.getAppID("tree")); this.getContentPane().setLayout(borderLayout1); this.setBackground(Color.white); this.setFont(new java.awt.Font("Verdana", 0, 12)); diff --git a/src/jalview/project/Jalview2XML.java b/src/jalview/project/Jalview2XML.java index 751b297..9303aed 100644 --- a/src/jalview/project/Jalview2XML.java +++ b/src/jalview/project/Jalview2XML.java @@ -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; @@ -60,6 +61,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; @@ -82,7 +84,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; @@ -162,10 +163,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; @@ -268,6 +269,25 @@ public class Jalview2XML private Map 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. @@ -427,7 +447,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") @@ -449,7 +469,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) { @@ -481,7 +501,7 @@ public class Jalview2XML return fref; } - public void resolveFrefedSequences() + protected void resolveFrefedSequences() { Iterator nextFref = frefedSequence.iterator(); int toresolve = frefedSequence.size(); @@ -626,13 +646,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 frames, JarOutputStream jout) { + Hashtable dsses = new Hashtable<>(); /* @@ -655,21 +676,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 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")) { @@ -677,11 +699,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); } } @@ -799,10 +827,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 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); @@ -829,7 +869,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 viewIds) { return saveState(ap, fileName, false, jout, viewIds); @@ -851,7 +891,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 viewIds) { if (viewIds == null) @@ -1083,7 +1123,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--) { @@ -1235,9 +1275,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++) { @@ -1281,9 +1321,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) { @@ -1924,11 +1964,11 @@ public class Jalview2XML final SequenceI jds, List 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) @@ -2384,7 +2424,7 @@ public class Jalview2XML if (calcIdParam.getVersion().equals("1.0")) { final String[] calcIds = calcIdParam.getServiceURL().toArray(new String[0]); - Jws2Instance service = Jws2Discoverer.getDiscoverer() + Jws2Instance service = Jws2Discoverer.getInstance() .getPreferredServiceFor(calcIds); if (service != null) { @@ -2723,17 +2763,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 @@ -2767,7 +2796,10 @@ public class Jalview2XML { try { - SwingUtilities.invokeAndWait(new Runnable() +// was invokeAndWait + + // BH 2019 -- can't wait + SwingUtilities.invokeLater(new Runnable() { @Override public void run() @@ -2780,55 +2812,55 @@ 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 String getFilename() + { + return file; + } + }; + } catch (IOException e) + { + e.printStackTrace(); + return null; + } + } /** * Recover jalview session from a jalview project archive. Caller may @@ -2854,12 +2886,19 @@ public class Jalview2XML IdentityHashMap importedDatasets = new IdentityHashMap<>(); Map gatherToThisFrame = new HashMap<>(); final String file = jprovider.getFilename(); + + List 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(); @@ -2867,9 +2906,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() @@ -2877,14 +2934,25 @@ public class Jalview2XML javax.xml.bind.Unmarshaller um = jc.createUnmarshaller(); JAXBElement 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, 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 @@ -2927,9 +2995,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) { @@ -2946,6 +3014,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); + } + } /* @@ -2957,7 +3032,7 @@ public class Jalview2XML */ for (AlignFrame fr : gatherToThisFrame.values()) { - Desktop.instance.gatherViews(fr); + Desktop.getInstance().gatherViews(fr); } restoreSplitFrames(); @@ -2965,8 +3040,7 @@ public class Jalview2XML { if (ds.getCodonFrames() != null) { - StructureSelectionManager - .getStructureSelectionManager(Desktop.instance) + Desktop.getStructureSelectionManager() .registerMappings(ds.getCodonFrames()); } } @@ -2975,9 +3049,9 @@ public class Jalview2XML reportErrors(); } - if (Desktop.instance != null) + if (Desktop.getInstance() != null) { - Desktop.instance.stopLoading(); + Desktop.getInstance().stopLoading(); } return af; @@ -3058,7 +3132,7 @@ public class Jalview2XML */ for (SplitFrame sf : gatherTo) { - Desktop.instance.gatherViews(sf); + Desktop.getInstance().gatherViews(sf); } splitFrameCandidates.clear(); @@ -3117,7 +3191,7 @@ public class Jalview2XML @Override public void run() { - JvOptionPane.showInternalMessageDialog(Desktop.desktop, + JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(), finalErrorMessage, "Error " + (saving ? "saving" : "loading") + " Jalview file", @@ -3178,25 +3252,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 { @@ -3276,7 +3350,9 @@ 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 @@ -3294,6 +3370,7 @@ public class Jalview2XML SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet().get(0); List vamsasSeqs = vamsasSet.getSequence(); + // JalviewModelSequence jms = object.getJalviewModelSequence(); // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0) @@ -3589,8 +3666,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) @@ -3603,6 +3679,7 @@ public class Jalview2XML } } } + } } // end !multipleview @@ -3740,6 +3817,7 @@ public class Jalview2XML } } } + // create the new AlignmentAnnotation jalview.datamodel.AlignmentAnnotation jaa = null; if (annotation.isGraph()) @@ -3776,6 +3854,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); @@ -3987,8 +4066,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) { @@ -4027,8 +4104,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 @@ -4038,25 +4116,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, uniqueSeqSetId, viewId, autoAlan); av = af.getViewport(); + // note that this only retrieves the most recently accessed + // tab of an AlignFrame. ap = af.alignPanel; } @@ -4065,14 +4145,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; } @@ -4089,7 +4216,7 @@ public class Jalview2XML * @param jseqs * @param ap */ - private void loadRnaViewers(jarInputStreamProvider jprovider, + protected void loadRnaViewers(jarInputStreamProvider jprovider, List jseqs, AlignmentPanel ap) { /* @@ -4199,6 +4326,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 ? @@ -4219,13 +4352,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); @@ -4308,7 +4434,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()); @@ -4550,7 +4676,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])); @@ -4627,9 +4754,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() @@ -4656,14 +4785,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(); + // } } @@ -4792,7 +4921,7 @@ public class Jalview2XML { try { - frames = Desktop.desktop.getAllFrames(); + frames = Desktop.getDesktopPane().getAllFrames(); } catch (ArrayIndexOutOfBoundsException e) { // occasional No such child exceptions are thrown here... @@ -4862,27 +4991,28 @@ public class Jalview2XML } } - AlignFrame loadViewport(String file, List JSEQ, - List hiddenSeqs, AlignmentI al, - JalviewModel jm, Viewport view, String uniqueSeqSetId, - String viewId, List autoAlan) + AlignFrame loadViewport(String fileName, List JSEQ, + List hiddenSeqs, AlignmentI al, JalviewModel jm, + Viewport view, String uniqueSeqSetId, String viewId, + List 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.setFileName(fileName, FileFormat.Jalview); + af.setFileObject(jarFile); // BH 2019 JAL-3436 final AlignViewport viewport = af.getViewport(); for (int i = 0; i < JSEQ.size(); i++) @@ -4957,9 +5087,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())); @@ -4990,8 +5119,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; @@ -5035,9 +5174,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); @@ -5057,17 +5195,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 featureColours = new Hashtable<>(); Map 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(); @@ -5079,8 +5217,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); @@ -5112,8 +5250,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( @@ -5147,8 +5284,7 @@ public class Jalview2XML } else { - featureColours.put(featureType, - new FeatureColour(maxColour)); + featureColours.put(featureType, new FeatureColour(maxColour)); } renderOrder[fs] = featureType; if (setting.getOrder() != null) @@ -5485,7 +5621,7 @@ public class Jalview2XML return false; } - public void addToSkipList(AlignFrame af) + protected void addToSkipList(AlignFrame af) { if (skipList == null) { @@ -5494,7 +5630,7 @@ public class Jalview2XML skipList.put(af.getViewport().getSequenceSetId(), af); } - public void clearSkipList() + protected void clearSkipList() { if (skipList != null) { @@ -5563,8 +5699,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. @@ -5994,6 +6130,8 @@ public class Jalview2XML AlignFrame af = loadFromObject(jm, 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; @@ -6351,7 +6489,11 @@ public class Jalview2XML panel.getRotatableCanvas().getAxisEndPoints()[i] = new Point( axis.getXPos(), axis.getYPos(), axis.getZPos()); } - PCAPanel.addToDesktop(panel, modelName); + + Dimension dim = Platform.getDimIfEmbedded(panel, 475, 450); + Desktop.addInternalFrame(panel, MessageManager.formatMessage( + "label.calc_title", "PCA", modelName), dim.width, + dim.height); } } catch (Exception ex) { diff --git a/src/jalview/rest/RestHandler.java b/src/jalview/rest/RestHandler.java index a37882f..7c8c9a6 100644 --- a/src/jalview/rest/RestHandler.java +++ b/src/jalview/rest/RestHandler.java @@ -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; } /** diff --git a/src/jalview/schemes/ColourSchemes.java b/src/jalview/schemes/ColourSchemes.java index d31fbba..0ff7c6e 100644 --- a/src/jalview/schemes/ColourSchemes.java +++ b/src/jalview/schemes/ColourSchemes.java @@ -21,24 +21,19 @@ 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 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 schemes; + + /** * Loads an instance of each standard or user-defined colour scheme * * @return diff --git a/src/jalview/structure/StructureImportSettings.java b/src/jalview/structure/StructureImportSettings.java index 9662fee..b5672ab 100644 --- a/src/jalview/structure/StructureImportSettings.java +++ b/src/jalview/structure/StructureImportSettings.java @@ -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()); } diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index 8c3816e..f8048cd 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -20,19 +20,10 @@ */ package jalview.structure; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Vector; - 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; @@ -57,16 +48,26 @@ import jalview.util.Platform; import jalview.ws.sifts.SiftsClient; import jalview.ws.sifts.SiftsException; import jalview.ws.sifts.SiftsSettings; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + 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 instances; - private List mappings = new ArrayList<>(); private boolean processSecondaryStructure = false; @@ -84,6 +85,67 @@ public class StructureSelectionManager private List sel_listeners = new ArrayList<>(); + /* + * instances of this class scoped by some context class + */ + private IdentityHashMap 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 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) @@ -652,7 +676,6 @@ public class StructureSelectionManager { ds = ds.getDatasetSequence(); } - ; if (ds.getAnnotation() != null) { for (AlignmentAnnotation ala : ds.getAnnotation()) @@ -906,9 +929,9 @@ public class StructureSelectionManager if (s != null) { result = s; - } } } + } return result; } @@ -1242,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() { @@ -1281,7 +1307,11 @@ public class StructureSelectionManager } } - public void addSelectionListener(SelectionListener selecter) + public List getListeners() { + return sel_listeners; + } + + public void addSelectionListener(SelectionListener selecter) { if (!sel_listeners.contains(selecter)) { @@ -1329,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 diff --git a/src/jalview/urls/IdOrgSettings.java b/src/jalview/urls/IdOrgSettings.java index 7dd1a19..7c38ab6 100644 --- a/src/jalview/urls/IdOrgSettings.java +++ b/src/jalview/urls/IdOrgSettings.java @@ -21,35 +21,46 @@ 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; } } diff --git a/src/jalview/urls/IdentifiersUrlProvider.java b/src/jalview/urls/IdentifiersUrlProvider.java index 07eb23e..e898438 100644 --- a/src/jalview/urls/IdentifiersUrlProvider.java +++ b/src/jalview/urls/IdentifiersUrlProvider.java @@ -118,11 +118,6 @@ private HashMap 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(); } diff --git a/src/jalview/util/MessageManager.java b/src/jalview/util/MessageManager.java index bb94566..2a07616 100644 --- a/src/jalview/util/MessageManager.java +++ b/src/jalview/util/MessageManager.java @@ -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); + } } } diff --git a/src/jalview/util/Platform.java b/src/jalview/util/Platform.java index 6dbe549..371cd32 100644 --- a/src/jalview/util/Platform.java +++ b/src/jalview/util/Platform.java @@ -22,9 +22,7 @@ package jalview.util; 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; @@ -34,8 +32,17 @@ 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; @@ -48,6 +55,7 @@ import org.json.simple.parser.ParseException; import com.stevesoft.pat.Regex; +import jalview.bin.Jalview; import jalview.javascript.json.JSON; import swingjs.api.JSUtilI; @@ -56,7 +64,6 @@ import swingjs.api.JSUtilI; * * @author Jim Procter */ - public class Platform { @@ -69,7 +76,6 @@ public class Platform private static Boolean isHeadless = null; private static swingjs.api.JSUtilI jsutil; - static { @@ -77,6 +83,9 @@ public class Platform { 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) @@ -230,14 +239,14 @@ public class Platform */ protected static boolean isControlDown(MouseEvent e, boolean aMac) { - + 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. // Notice the distinction in mouse events. (BUTTON3_MASK == META) @@ -638,9 +647,14 @@ public class Platform } + /** + * Allow for URL-line command arguments. Untested. + * + */ public static void getURLCommandArguments() { + 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 @@ -650,8 +664,11 @@ public class Platform * 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) { + } } /** @@ -804,17 +821,17 @@ public class Platform { if (isJS) { - jsutil.setAppletAttribute("app", j); + jsutil.setAppClass(j); } } /** * - * If this frame ia embedded in a web page, return a known type. + * If this frame is embedded in a web page, return a known type. * * @param frame * a JFrame or JInternalFrame - * @param type + * @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) @@ -844,4 +861,160 @@ public class Platform 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 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()); + } + } + } + } diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 4f399f0..4148c72 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -83,6 +83,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(); @@ -605,10 +608,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(); @@ -824,7 +848,7 @@ public abstract class AlignmentViewport // see note in mantis : issue number 8585 if (alignment.isNucleotide() || (conservation == null && quality == null) - || !autoCalculateConsensus) + || !autoCalculateConsensusAndConservation) { return; } @@ -842,7 +866,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; } @@ -1275,21 +1299,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; } @@ -1350,22 +1375,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() @@ -1430,13 +1439,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(); @@ -1459,8 +1469,7 @@ public abstract class AlignmentViewport } ranges.setStartEndSeq(startSeq, endSeq + tmp.size()); - - firePropertyChange("alignment", null, alignment.getSequences()); + notifyAlignment(); sendSelection(); } } @@ -1494,7 +1503,7 @@ public abstract class AlignmentViewport setSequenceAnnotationsVisible(seq[i], false); } ranges.setStartSeq(startSeq); - firePropertyChange("alignment", null, alignment.getSequences()); + notifyAlignment(); } } @@ -1859,11 +1868,11 @@ public abstract class AlignmentViewport { alignment.padGaps(); } - if (autoCalculateConsensus) + if (autoCalculateConsensusAndConservation) { updateConsensus(ap); } - if (hconsensus != null && autoCalculateConsensus) + if (hconsensus != null && autoCalculateConsensusAndConservation) { updateConservation(ap); } @@ -3078,4 +3087,22 @@ public abstract class AlignmentViewport codingComplement.setUpdateStructures(needToUpdateStructureViews); } } + + + /** + * 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); + } + } diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index 4f671da..b96ac28 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -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 + "]"; + } } diff --git a/src/jalview/workers/AlignCalcManager.java b/src/jalview/workers/AlignCalcManager.java index 08ef3a2..c596f05 100644 --- a/src/jalview/workers/AlignCalcManager.java +++ b/src/jalview/workers/AlignCalcManager.java @@ -207,6 +207,10 @@ public class AlignCalcManager implements AlignCalcManagerI } } + public int getQueueLength() { + return inProgress.size(); + } + @Override public void registerWorker(AlignCalcWorkerI worker) { diff --git a/src/jalview/ws/SequenceFetcher.java b/src/jalview/ws/SequenceFetcher.java index a480176..5c94faf 100644 --- a/src/jalview/ws/SequenceFetcher.java +++ b/src/jalview/ws/SequenceFetcher.java @@ -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 index 9cc4960..25144fc 100644 --- a/src/jalview/ws/SequenceFetcherFactory.java +++ b/src/jalview/ws/SequenceFetcherFactory.java @@ -24,29 +24,33 @@ 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; - } + // BH the two methods in this class merged into SequenceFetcher } +//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; +// } +//} diff --git a/src/jalview/ws/jws1/Discoverer.java b/src/jalview/ws/jws1/Discoverer.java index bee9fad..6aa3f48 100644 --- a/src/jalview/ws/jws1/Discoverer.java +++ b/src/jalview/ws/jws1/Discoverer.java @@ -20,11 +20,15 @@ */ package jalview.ws.jws1; +import jalview.bin.ApplicationSingletonProvider; +import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI; +import jalview.gui.AlignmentPanel; import jalview.gui.JvOptionPane; import jalview.util.MessageManager; import java.net.URL; import java.util.Hashtable; +import java.util.List; import java.util.StringTokenizer; import java.util.Vector; @@ -34,8 +38,19 @@ import ext.vamsas.RegistryServiceSoapBindingStub; import ext.vamsas.ServiceHandle; import ext.vamsas.ServiceHandles; -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( @@ -110,20 +125,24 @@ public class Discoverer implements Runnable return server; } - static private java.net.URL RootServiceURL = null; + private java.net.URL RootServiceURL = null; - static public Vector ServiceURLList = null; + private Vector ServiceURLList = null; - static private boolean reallyDiscoverServices = true; + public Vector getServiceURLList() { + return ServiceURLList; + } + + private boolean reallyDiscoverServices = true; - public static java.util.Hashtable> services = null; + private java.util.Hashtable> services = null; // stored by // abstractServiceType // string - public static java.util.Vector serviceList = null; + public java.util.Vector serviceList = null; - static private Vector getDiscoveryURLS() + private Vector getDiscoveryURLS() { Vector urls = new Vector<>(); String RootServiceURLs = jalview.bin.Cache.getDefault("DISCOVERY_URLS", @@ -176,10 +195,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) @@ -246,9 +271,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"), @@ -284,7 +309,7 @@ public class Discoverer implements Runnable * Hashtable * @return boolean */ - static private boolean buildServiceLists(ServiceHandle[] sh, + private boolean buildServiceLists(ServiceHandle[] sh, Vector cat, Hashtable> sscat) { @@ -395,10 +420,32 @@ public class Discoverer implements Runnable /** * binding service abstract name to handler class */ - private static Hashtable serviceClientBindings; + private Hashtable 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 @@ -421,20 +468,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> getServices() + { + return getInstance().services; + } } diff --git a/src/jalview/ws/jws1/JPredClient.java b/src/jalview/ws/jws1/JPredClient.java index 3b7bdb6..1c625fa 100644 --- a/src/jalview/ws/jws1/JPredClient.java +++ b/src/jalview/ws/jws1/JPredClient.java @@ -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[] diff --git a/src/jalview/ws/jws1/MsaWSClient.java b/src/jalview/ws/jws1/MsaWSClient.java index 4a09625..e97d309 100644 --- a/src/jalview/ws/jws1/MsaWSClient.java +++ b/src/jalview/ws/jws1/MsaWSClient.java @@ -78,7 +78,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[] @@ -91,7 +91,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"), diff --git a/src/jalview/ws/jws1/SeqSearchWSClient.java b/src/jalview/ws/jws1/SeqSearchWSClient.java index 53338d3..cb965f0 100644 --- a/src/jalview/ws/jws1/SeqSearchWSClient.java +++ b/src/jalview/ws/jws1/SeqSearchWSClient.java @@ -84,7 +84,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[] @@ -97,7 +97,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() }), diff --git a/src/jalview/ws/jws1/WS1Client.java b/src/jalview/ws/jws1/WS1Client.java index 6fa41fc..7bffe43 100644 --- a/src/jalview/ws/jws1/WS1Client.java +++ b/src/jalview/ws/jws1/WS1Client.java @@ -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; } diff --git a/src/jalview/ws/jws2/Jws2Client.java b/src/jalview/ws/jws2/Jws2Client.java index 0f1a25e..004a845 100644 --- a/src/jalview/ws/jws2/Jws2Client.java +++ b/src/jalview/ws/jws2/Jws2Client.java @@ -154,8 +154,8 @@ public abstract class Jws2Client extends jalview.ws.WSClient return new WebserviceInfo(WebServiceJobTitle, WebServiceJobTitle + " using service hosted at " + serv.hosturl + "\n" - + (serv.description != null ? serv.description : ""), - false); + + (serv.description != null ? serv.description : ""), + Desktop.FRAME_NOT_VISIBLE); } return null; } @@ -398,7 +398,7 @@ public abstract class Jws2Client extends jalview.ws.WSClient { // check service is actually in the list of currently avaialable // services - if (!Jws2Discoverer.getDiscoverer().getServices().contains(service)) + if (!Jws2Discoverer.getInstance().getServices().contains(service)) { // it isn't .. service = null; @@ -408,7 +408,7 @@ public abstract class Jws2Client extends jalview.ws.WSClient if (service == null) { // get the default service for AACon - service = Jws2Discoverer.getDiscoverer().getPreferredServiceFor(null, + service = Jws2Discoverer.getInstance().getPreferredServiceFor(null, aaui.getServiceType()); } if (service == null) diff --git a/src/jalview/ws/jws2/Jws2Discoverer.java b/src/jalview/ws/jws2/Jws2Discoverer.java index 516a719..255ef8f 100644 --- a/src/jalview/ws/jws2/Jws2Discoverer.java +++ b/src/jalview/ws/jws2/Jws2Discoverer.java @@ -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.gui.Desktop; import jalview.gui.JvSwingUtils; @@ -60,8 +62,29 @@ import compbio.ws.client.Services; * @author JimP * */ -public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI +public class Jws2Discoverer + implements Runnable, WSMenuEntryProviderI, ApplicationSingletonI { + + /** + * Returns the singleton instance of this class. + * + * @return + */ + public static Jws2Discoverer getInstance() + { + return (Jws2Discoverer) ApplicationSingletonProvider + .getInstance(Jws2Discoverer.class); + } + + /** + * Private constructor enforces use of singleton via getDiscoverer() + */ + private Jws2Discoverer() + { + // use getInstance(); + } + public static final String COMPBIO_JABAWS = "http://www.compbio.dundee.ac.uk/jabaws"; /* @@ -70,19 +93,14 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI private final static String JWS2HOSTURLS = "JWS2HOSTURLS"; /* - * Singleton instance - */ - private static Jws2Discoverer discoverer; - - /* * Override for testing only */ - private static List testUrls = null; + private List testUrls = null; // preferred url has precedence over others private String preferredUrl; - private PropertyChangeSupport changeSupport = new PropertyChangeSupport( + protected PropertyChangeSupport changeSupport = new PropertyChangeSupport( this); private Vector invalidServiceUrls = null; @@ -103,13 +121,6 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI protected Vector services; /** - * Private constructor enforces use of singleton via getDiscoverer() - */ - private Jws2Discoverer() - { - } - - /** * change listeners are notified of "services" property changes * * @param listener @@ -190,7 +201,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI oldthread = Thread.currentThread(); try { - Class foo = getClass().getClassLoader() + getClass().getClassLoader() .loadClass("compbio.ws.client.Jws2Client"); } catch (ClassNotFoundException e) { @@ -583,36 +594,36 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI */ 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); } } - Thread runner = getDiscoverer() - .startDiscoverer(new PropertyChangeListener() - { + Thread runner = instance.startDiscoverer(new PropertyChangeListener() + { - @Override - public void propertyChange(PropertyChangeEvent evt) - { - if (getDiscoverer().services != null) - { - System.out.println("Changesupport: There are now " - + getDiscoverer().services.size() + " services"); - int i = 1; - for (Jws2Instance instance : getDiscoverer().services) - { - System.out.println("Service " + i++ + " " - + instance.getClass() + "@" + instance.getHost() - + ": " + instance.getActionText()); - } + @Override + public void propertyChange(PropertyChangeEvent evt) + { + if (getInstance().services != null) + { + System.out.println("Changesupport: There are now " + + getInstance().services.size() + " services"); + int i = 1; + for (Jws2Instance instance : getInstance().services) + { + System.out.println("Service " + i++ + " " + instance.getClass() + + "@" + instance.getHost() + ": " + + instance.getActionText()); + } - } - } - }); + } + } + }); while (runner.isAlive()) { try @@ -630,20 +641,6 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI } } - /** - * Returns the singleton instance of this class. - * - * @return - */ - public static Jws2Discoverer getDiscoverer() - { - if (discoverer == null) - { - discoverer = new Jws2Discoverer(); - } - return discoverer; - } - public boolean hasServices() { return !running && services != null && services.size() > 0; diff --git a/src/jalview/ws/jws2/MsaWSClient.java b/src/jalview/ws/jws2/MsaWSClient.java index 23c6949..319252a 100644 --- a/src/jalview/ws/jws2/MsaWSClient.java +++ b/src/jalview/ws/jws2/MsaWSClient.java @@ -107,7 +107,7 @@ public class MsaWSClient extends Jws2Client if (!(sh.service instanceof MsaWS)) { // 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[] @@ -120,7 +120,7 @@ public class MsaWSClient extends Jws2Client server = (MsaWS) sh.service; 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.serviceType }), MessageManager.getString("label.internal_jalview_error"), diff --git a/src/jalview/ws/jws2/SequenceAnnotationWSClient.java b/src/jalview/ws/jws2/SequenceAnnotationWSClient.java index 45bddac..67e3338 100644 --- a/src/jalview/ws/jws2/SequenceAnnotationWSClient.java +++ b/src/jalview/ws/jws2/SequenceAnnotationWSClient.java @@ -242,7 +242,7 @@ public class SequenceAnnotationWSClient extends Jws2Client @Override public void actionPerformed(ActionEvent arg0) { - Desktop.instance.showUrl(service.docUrl); + Desktop.getInstance().showUrl(service.docUrl); } }); annotservice.setToolTipText( diff --git a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java index e092192..7954db0 100644 --- a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java +++ b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java @@ -186,7 +186,7 @@ public class Jws2Instance implements AutoCloseable try { paramStore = new JabaParamStore(this, - (Desktop.instance != null ? Desktop.getUserParameterStore() + (Desktop.getInstance() != null ? Desktop.getUserParameterStore() : null)); } catch (Exception ex) { diff --git a/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java b/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java index 623b8de..1983ff5 100644 --- a/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java +++ b/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java @@ -20,6 +20,8 @@ */ package jalview.ws.jws2.jabaws2; +import jalview.bin.ApplicationSingletonProvider; +import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI; import jalview.ws.jws2.AAConClient; import jalview.ws.jws2.RNAalifoldClient; import jalview.ws.uimodel.AlignAnalysisUIText; @@ -29,11 +31,23 @@ import java.util.HashSet; import compbio.data.msa.JABAService; -public class Jws2InstanceFactory +public class Jws2InstanceFactory implements ApplicationSingletonI { - private static HashMap aaConGUI; - private static HashSet ignoreGUI; + private Jws2InstanceFactory() + { + // private singleton + } + + private static Jws2InstanceFactory getInstance() + { + return (Jws2InstanceFactory) ApplicationSingletonProvider + .getInstance(Jws2InstanceFactory.class); + } + + private HashMap aaConGUI; + + private HashSet ignoreGUI; private static String category_rewrite(String cat_name) { @@ -42,17 +56,17 @@ public class Jws2InstanceFactory : cat_name; } - private static void init() + private void init() { if (aaConGUI == null) { - aaConGUI = new HashMap(); + aaConGUI = new HashMap<>(); aaConGUI.put(compbio.ws.client.Services.AAConWS.toString(), AAConClient.getAlignAnalysisUITest()); aaConGUI.put(compbio.ws.client.Services.RNAalifoldWS.toString(), RNAalifoldClient.getAlignAnalysisUITest()); // ignore list for JABAWS services not supported in jalview ... - ignoreGUI = new HashSet(); + ignoreGUI = new HashSet<>(); } } @@ -65,8 +79,8 @@ public class Jws2InstanceFactory */ public static boolean ignoreService(String serviceType) { - init(); - return (ignoreGUI.contains(serviceType.toString())); + getInstance().init(); + return (getInstance().ignoreGUI.contains(serviceType.toString())); } /** @@ -84,10 +98,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.aaui = aaConGUI.get(serviceType.toString()); + svc.aaui = getInstance().aaConGUI.get(serviceType.toString()); return svc; } diff --git a/src/jalview/ws/rest/RestClient.java b/src/jalview/ws/rest/RestClient.java index a71b70d..5d1a288 100644 --- a/src/jalview/ws/rest/RestClient.java +++ b/src/jalview/ws/rest/RestClient.java @@ -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; @@ -49,8 +51,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 services = null; + + RestServiceDescription service; public RestClient(RestServiceDescription rsd) @@ -109,7 +130,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); } @@ -329,7 +350,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")), @@ -399,12 +420,13 @@ public class RestClient extends WSClient return true; } - protected static Vector 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(); @@ -458,10 +480,11 @@ public class RestClient extends WSClient { if (rsbsUrls != null) { + // TODO: consider validating services ? - services = new Vector(rsbsUrls); + getInstance().services = new Vector(rsbsUrls); StringBuffer sprop = new StringBuffer(); - for (String s : services) + for (String s : getInstance().services) { sprop.append(s); } diff --git a/src/jalview/ws/sifts/SiftsClient.java b/src/jalview/ws/sifts/SiftsClient.java index 4fb9ca9..3ec6320 100644 --- a/src/jalview/ws/sifts/SiftsClient.java +++ b/src/jalview/ws/sifts/SiftsClient.java @@ -22,20 +22,15 @@ package jalview.ws.sifts; 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; @@ -101,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; @@ -117,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 curDBRefAccessionIdsString; + private boolean doCache = false; + private enum CoordinateSys { UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe"); @@ -165,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()); + } } /** @@ -180,8 +201,17 @@ 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"); @@ -190,10 +220,6 @@ public class SiftsClient implements SiftsClientI Unmarshaller um = jc.createUnmarshaller(); JAXBElement jbe = um.unmarshal(streamReader, Entry.class); return jbe.getValue(); - } catch (Exception e) - { - e.printStackTrace(); - throw new SiftsException(e.getMessage()); } } @@ -223,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) @@ -247,7 +273,7 @@ public class SiftsClient implements SiftsClientI } try { - siftsFile = downloadSiftsFile(pdbId.toLowerCase()); + siftsFile = downloadSiftsFile(pdbId); } catch (IOException e) { throw new SiftsException(e.getMessage()); @@ -256,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 @@ -294,53 +291,47 @@ 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; - - /* - * Download the file from URL to either - * Java: directory of cached downloaded SIFTS files - * Javascript: temporary 'file' (in-memory cache) - */ + File downloadTo = null; - if (Platform.isJS()) - { - downloadTo = File.createTempFile(siftFile, ".xml.gz"); - } - else + if (asFile) { downloadTo = new File( SiftsSettings.getSiftDownloadDirectory() + siftFile); - File siftsDownloadDir = new File( - SiftsSettings.getSiftDownloadDirectory()); + 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( - downloadTo); - 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"); + 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; } @@ -648,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 cRefDbs = residue.getCrossRefDb(); @@ -660,7 +651,7 @@ public class SiftsClient implements SiftsClientI pdbRefDb = cRefDb; if (firstPDBResNum == UNASSIGNED) { - firstPDBResNum = getLeadingIntegerValue(cRefDb.getDbResNum(), + firstPDBResNum = Platform.getLeadingIntegerValue(cRefDb.getDbResNum(), UNASSIGNED); } else @@ -675,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) { @@ -725,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) @@ -748,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. diff --git a/src/jalview/ws/sifts/SiftsSettings.java b/src/jalview/ws/sifts/SiftsSettings.java index 5e2c526..029f48d 100644 --- a/src/jalview/ws/sifts/SiftsSettings.java +++ b/src/jalview/ws/sifts/SiftsSettings.java @@ -20,59 +20,79 @@ */ package jalview.ws.sifts; +import jalview.bin.ApplicationSingletonProvider; +import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI; +import jalview.util.Platform; + import java.util.Objects; -public class SiftsSettings +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); } } diff --git a/src/jalview/ws/utils/UrlDownloadClient.java b/src/jalview/ws/utils/UrlDownloadClient.java index 58632f2..227fe23 100644 --- a/src/jalview/ws/utils/UrlDownloadClient.java +++ b/src/jalview/ws/utils/UrlDownloadClient.java @@ -53,64 +53,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 index b57528b..3650544 100644 --- a/src/javajs/async/Assets.java +++ b/src/javajs/async/Assets.java @@ -1,5 +1,6 @@ package javajs.async; +import java.awt.Toolkit; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -361,7 +362,7 @@ public class Assets { try { URL url = getInstance()._getURLFromPath(path, true); if (url == null && !zipOnly) { - url = Assets.class.getResource(path); + url = Assets.class.getClassLoader().getResource(path); } if (url != null) return url.openStream(); diff --git a/src/javajs/async/AsyncSwingWorker.java b/src/javajs/async/AsyncSwingWorker.java index df103cd..703f7f5 100644 --- a/src/javajs/async/AsyncSwingWorker.java +++ b/src/javajs/async/AsyncSwingWorker.java @@ -10,6 +10,8 @@ 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. * @@ -38,18 +40,56 @@ import javajs.async.SwingJSUtils.StateMachine; * the subclass to update the progress field in both the SwingWorker and the * ProgressMonitor. * - * If it is desired to run the AsyncSwingWorker synchonously, call the + * 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 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; @@ -119,10 +159,12 @@ public abstract class AsyncSwingWorker extends SwingWorker implement } 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 { @@ -244,7 +286,7 @@ public abstract class AsyncSwingWorker extends SwingWorker implement 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() { @@ -255,6 +297,9 @@ public abstract class AsyncSwingWorker extends SwingWorker implement protected void setPaused(boolean tf) { isPaused = tf; + firePropertyChange(PROPERTY_PAUSE, null, (tf ? PAUSED : RESUMED)); + if (!tf) + stateLoop(); } protected boolean isPaused() { @@ -292,7 +337,7 @@ public abstract class AsyncSwingWorker extends SwingWorker implement case STATE_LOOP: if (checkCanceled()) { helper.setState(STATE_DONE); - firePropertyChange("state", null, CANCELED_ASYNC); + firePropertyChange(PROPERTY_STATE, null, CANCELED_ASYNC); } else { int ret = doInBackgroundAsync(progressAsync); if (!helper.isAlive() || isPaused) { @@ -309,6 +354,7 @@ public abstract class AsyncSwingWorker extends SwingWorker implement } continue; case STATE_WAIT: + // meaning "sleep" and then "loop" helper.setState(STATE_LOOP); helper.sleep(delayMillis); return true; @@ -343,7 +389,7 @@ public abstract class AsyncSwingWorker extends SwingWorker implement @Override public void run() { doneAsync(); - firePropertyChange("state", null, DONE_ASYNC); + firePropertyChange(PROPERTY_STATE, null, DONE_ASYNC); } }; diff --git a/src/swingjs/api/JSUtilI.java b/src/swingjs/api/JSUtilI.java index 1b6ff9b..322ec83 100644 --- a/src/swingjs/api/JSUtilI.java +++ b/src/swingjs/api/JSUtilI.java @@ -7,6 +7,7 @@ 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; @@ -91,10 +92,10 @@ public interface JSUtilI { /** - * Get an attribute of applet's Info map for the applet found using - * getApplet(null). That is, applet.__Info[InfoKey]. + * 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 + * @param infoKey if null, return the full __Info map */ Object getAppletInfo(String infoKey); @@ -329,4 +330,25 @@ public interface JSUtilI { */ 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 getAppletInfoAsMap(); + + + void setAppClass(Object j); + } diff --git a/swingjs/SwingJS-site.zip b/swingjs/SwingJS-site.zip index d5e28c1..5e9828e 100644 Binary files a/swingjs/SwingJS-site.zip and b/swingjs/SwingJS-site.zip differ diff --git a/swingjs/differences.txt b/swingjs/differences.txt new file mode 100644 index 0000000..70eabbc --- /dev/null +++ b/swingjs/differences.txt @@ -0,0 +1,1480 @@ +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 + + + + + + +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: + + [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
        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 ) +JTextArea (JavaScript