From: Jim Procter Date: Mon, 13 Nov 2017 18:59:59 +0000 (+0000) Subject: Merge branch 'features/JAL-2758_shorteruniprot' into develop X-Git-Tag: Release_2_10_3b1~35 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=141abbd1a196f26a643318ed9677d0225c568402;hp=b5fb170aae86089a49a0529dbb4c04bf36e0499b;p=jalview.git Merge branch 'features/JAL-2758_shorteruniprot' into develop --- diff --git a/.checkstyle b/.checkstyle index 0329bb7..a87fa04 100644 --- a/.checkstyle +++ b/.checkstyle @@ -8,5 +8,4 @@ - diff --git a/.classpath b/.classpath index c4a2832..d704f10 100644 --- a/.classpath +++ b/.classpath @@ -68,6 +68,6 @@ - + diff --git a/README b/README index cbc93b1..eaf226b 100755 --- a/README +++ b/README @@ -25,7 +25,10 @@ To run application: java -Djava.ext.dirs=JALVIEW_HOME/lib -cp JALVIEW_HOME/jalview.jar jalview.bin.Jalview -Replace JALVIEW_HOME with the full path to Jalview Installation Directory. +Replace JALVIEW_HOME with the full path to Jalview Installation Directory. If building from source: + +java -Djava.ext.dirs=JALVIEW_BUILD/dist -cp JALVIEW_BUILD/dist/jalview.jar jalview.bin.Jalview + ################## diff --git a/RELEASE b/RELEASE index cecefec..f1faf34 100644 --- a/RELEASE +++ b/RELEASE @@ -1,2 +1,2 @@ -jalview.release=releases/Release_2_10_2b1_Branch -jalview.version=2.10.2b2 +jalview.release=releases/Release_2_10_3_Branch +jalview.version=2.10.3 diff --git a/benchmarking/README b/benchmarking/README index 60b94a9..fac0bf6 100644 --- a/benchmarking/README +++ b/benchmarking/README @@ -1,11 +1,13 @@ To set up benchmarking: -1. In the jalview directory run +You will need to install Maven: https://maven.apache.org/install.html + +1. Run the makedist target of build.xml in Eclipse, or in the jalview directory run ant makedist This builds a jalview.jar file and puts it into dist/ -2. Make a lib directory in benchmarking/ if not already present. +2. Make a lib directory in benchmarking/ if not already present and cd into this directory. 3. Purge any previous maven dependencies: mvn dependency:purge-local-repository -DactTransitively=false -DreResolve=false diff --git a/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java b/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java index eb35e3b..d3c67d7 100644 --- a/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java +++ b/benchmarking/src/main/java/org/jalview/HiddenColumnsBenchmark.java @@ -47,126 +47,128 @@ import jalview.datamodel.HiddenColumns; @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Fork(1) public class HiddenColumnsBenchmark -{ - /* - * State with multiple hidden columns and a start position set - */ - @State(Scope.Thread) - public static class HiddenColsAndStartState - { - @Param({"300", "10000", "100000"}) - public int maxcols; - - @Param({"1", "50", "90"}) - public int startpcnt; // position as percentage of maxcols - - @Param({"1","15","100"}) - public int hide; - - HiddenColumns h = new HiddenColumns(); - Random rand = new Random(); - - public int hiddenColumn; - public int visibleColumn; - - @Setup - public void setup() - { - rand.setSeed(1234); - int lastcol = 0; - while (lastcol < maxcols) - { - int count = rand.nextInt(100); - lastcol += count; - h.hideColumns(lastcol, lastcol+hide); - lastcol+=hide; - } - - // make sure column at start is hidden - hiddenColumn = (int)(maxcols * startpcnt/100.0); - h.hideColumns(hiddenColumn, hiddenColumn); - - // and column after start is visible - ColumnSelection sel = new ColumnSelection(); - h.revealHiddenColumns(hiddenColumn+hide, sel); - visibleColumn = hiddenColumn+hide; - - System.out.println("Maxcols: " + maxcols + " HiddenCol: " + hiddenColumn + " Hide: " + hide); - System.out.println("Number of hidden columns: " + h.getSize()); - } - } - - /* Convention: functions in alphabetical order */ - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public int benchAdjustForHiddenColumns(HiddenColsAndStartState tstate) - { - return tstate.h.adjustForHiddenColumns(tstate.visibleColumn); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public int benchFindColumnPosition(HiddenColsAndStartState tstate) - { - return tstate.h.findColumnPosition(tstate.visibleColumn); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public List benchFindHiddenRegionPositions(HiddenColsAndStartState tstate) - { - return tstate.h.findHiddenRegionPositions(); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public ArrayList benchGetHiddenColumnsCopy(HiddenColsAndStartState tstate) - { - return tstate.h.getHiddenColumnsCopy(); - } - - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public int benchGetSize(HiddenColsAndStartState tstate) - { - return tstate.h.getSize(); - } +{ + /* + * State with multiple hidden columns and a start position set + */ + @State(Scope.Thread) + public static class HiddenColsAndStartState + { + @Param({ "300", "10000", "100000" }) + public int maxcols; - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public HiddenColumns benchHideCols(HiddenColsAndStartState tstate) - { - tstate.h.hideColumns(tstate.visibleColumn, - tstate.visibleColumn+2000); - return tstate.h; - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public boolean benchIsVisible(HiddenColsAndStartState tstate) - { - return tstate.h.isVisible(tstate.hiddenColumn); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public HiddenColumns benchReveal(HiddenColsAndStartState tstate) - { - ColumnSelection sel = new ColumnSelection(); - tstate.h.revealHiddenColumns(tstate.hiddenColumn, sel); - return tstate.h; - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - public HiddenColumns benchRevealAll(HiddenColsAndStartState tstate) + @Param({ "1", "50", "90" }) + public int startpcnt; // position as percentage of maxcols + + @Param({ "1", "15", "100" }) + public int hide; + + HiddenColumns h = new HiddenColumns(); + + Random rand = new Random(); + + public int hiddenColumn; + + public int visibleColumn; + + @Setup + public void setup() { - ColumnSelection sel = new ColumnSelection(); - tstate.h.revealAllHiddenColumns(sel); - return tstate.h; + rand.setSeed(1234); + int lastcol = 0; + while (lastcol < maxcols) + { + int count = rand.nextInt(100); + lastcol += count; + h.hideColumns(lastcol, lastcol + hide); + lastcol += hide; + } + + // make sure column at start is hidden + hiddenColumn = (int) (maxcols * startpcnt / 100.0); + h.hideColumns(hiddenColumn, hiddenColumn); + + // and column after start is visible + ColumnSelection sel = new ColumnSelection(); + h.revealHiddenColumns(hiddenColumn + hide, sel); + visibleColumn = hiddenColumn + hide; + + System.out.println("Maxcols: " + maxcols + " HiddenCol: " + + hiddenColumn + " Hide: " + hide); + System.out.println("Number of hidden columns: " + h.getSize()); } - - + } + + /* Convention: functions in alphabetical order */ + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public int benchAdjustForHiddenColumns(HiddenColsAndStartState tstate) + { + return tstate.h.adjustForHiddenColumns(tstate.visibleColumn); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public int benchFindColumnPosition(HiddenColsAndStartState tstate) + { + return tstate.h.findColumnPosition(tstate.visibleColumn); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public List benchFindHiddenRegionPositions( + HiddenColsAndStartState tstate) + { + return tstate.h.findHiddenRegionPositions(); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public ArrayList benchGetHiddenColumnsCopy( + HiddenColsAndStartState tstate) + { + return tstate.h.getHiddenColumnsCopy(); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public int benchGetSize(HiddenColsAndStartState tstate) + { + return tstate.h.getSize(); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public HiddenColumns benchHideCols(HiddenColsAndStartState tstate) + { + tstate.h.hideColumns(tstate.visibleColumn, tstate.visibleColumn + 2000); + return tstate.h; + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public boolean benchIsVisible(HiddenColsAndStartState tstate) + { + return tstate.h.isVisible(tstate.hiddenColumn); + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public HiddenColumns benchReveal(HiddenColsAndStartState tstate) + { + ColumnSelection sel = new ColumnSelection(); + tstate.h.revealHiddenColumns(tstate.hiddenColumn, sel); + return tstate.h; + } + + @Benchmark + @BenchmarkMode({ Mode.Throughput }) + public HiddenColumns benchRevealAll(HiddenColsAndStartState tstate) + { + ColumnSelection sel = new ColumnSelection(); + tstate.h.revealAllHiddenColumns(sel); + return tstate.h; + } + } \ No newline at end of file diff --git a/build.xml b/build.xml index eb30ef0..4931cfb 100755 --- a/build.xml +++ b/build.xml @@ -425,7 +425,7 @@ - + @@ -450,9 +450,9 @@ - - - j2se version="1.9+" + + + j2se version="1.7+" diff --git a/examples/groovy/PIDmatrix.groovy b/examples/groovy/PIDmatrix.groovy new file mode 100644 index 0000000..b97abcc --- /dev/null +++ b/examples/groovy/PIDmatrix.groovy @@ -0,0 +1,99 @@ +/* + * 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. + */ + +import jalview.analysis.scoremodels.ScoreModels +import jalview.analysis.scoremodels.SimilarityParams + +// generate matrix for current selection using standard Jalview PID + +printSimilarityMatrix(true,true,SimilarityParams.Jalview) + +/** + * this function prints a sequence similarity matrix in PHYLIP format. + * printSimilarityMatrix(selected-only, include-ids, pidMethod) + * + * Allowed values for pidMethod: + * + * Jalview's Comparison.PID method includes matching gaps + * and counts over the length of the shorter gapped sequence + * SimilarityParams.Jalview; + * + * 'SeqSpace' mode PCA calculation does not count matching + * gaps but uses longest gapped sequence length + * SimilarityParams.SeqSpace; + * + * PID calcs from the Raghava-Barton paper + * SimilarityParams.PID1: ignores gap-gap, does not score gap-residue, + * includes gap-residue in lengths, matches on longer of two sequences. + * + * SimilarityParams.PID2: ignores gap-gap,ignores gap-residue, + * matches on longer of two sequences + * + * SimilarityParams.PID3: ignores gap-gap,ignores gap-residue, + * matches on shorter of sequences only + * + * SimilarityParams.PID4: ignores gap-gap,does not score gap-residue, + * includes gap-residue in lengths,matches on shorter of sequences only. + */ + +void printSimilarityMatrix(boolean selview=false, boolean includeids=true, SimilarityParams pidMethod) { + + def currentAlignFrame = jalview.bin.Jalview.getCurrentAlignFrame() + + jalview.gui.AlignViewport av = currentAlignFrame.getCurrentView() + + jalview.datamodel.AlignmentView seqStrings = av.getAlignmentView(selview) + + if (!selview || av.getSelectionGroup()==null) { + start = 0 + end = av.getAlignment().getWidth() + seqs = av.getAlignment().getSequencesArray() + } else { + start = av.getSelectionGroup().getStartRes() + end = av.getSelectionGroup().getEndRes() + 1 + seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment()) + } + + distanceCalc = ScoreModels.getInstance().getScoreModel("PID", + (jalview.api.AlignmentViewPanel) currentAlignFrame.alignPanel) + + def distance=distanceCalc.findSimilarities( + seqStrings.getSequenceStrings(jalview.util.Comparison.GAP_DASH),pidMethod) + + // output the PHYLIP Matrix + + print distance.width()+" "+distance.height()+"\n" + + p = 0 + + for (v in 1..distance.height()) { + + if (includeids) { + print seqs[p++].getDisplayId(false)+" " + } + + for (r in 1..distance.width()) { + print distance.getValue(v-1,r-1)+" " + } + + print "\n" + } +} \ No newline at end of file diff --git a/help/helpTOC.xml b/help/helpTOC.xml index 4636ea3..7ba4ee5 100755 --- a/help/helpTOC.xml +++ b/help/helpTOC.xml @@ -24,13 +24,7 @@ - - - - - - - + diff --git a/help/html/calculations/pairwise.html b/help/html/calculations/pairwise.html index bb80b84..1090253 100755 --- a/help/html/calculations/pairwise.html +++ b/help/html/calculations/pairwise.html @@ -38,11 +38,21 @@

Gap open : 12
Gap extend : 2

-

When you select the pairwise alignment option a new window will - come up which will display the alignments in a text format as they - are calculated. Also displayed is information about the alignment - such as alignment score, length and percentage identity between the +

When you select the pairwise alignment option, a new window + will come up which displays the alignments in a text format, for + example:

+

+

+    FER1_SPIOL/5-13 TTMMGMAT
+ |. .. ||
+ FER1_MESCR/5-15 TAALSGAT +
+ shows the aligned sequences, where '|' links identical residues, and + (for peptide) '.' links residues that have a positive PAM250 score. +

The window also shows information about the alignment such as + alignment score, length and percentage identity between the sequences.

-

 

+

A button is also provided to allow you to view the sequences as + an alignment.

diff --git a/help/html/features/pdbseqfetcher.png b/help/html/features/pdbseqfetcher.png index 97a779a..2081a3d 100644 Binary files a/help/html/features/pdbseqfetcher.png and b/help/html/features/pdbseqfetcher.png differ diff --git a/help/html/features/pdbsequencefetcher.html b/help/html/features/pdbsequencefetcher.html index 2962ba6..bb63bed 100644 --- a/help/html/features/pdbsequencefetcher.html +++ b/help/html/features/pdbsequencefetcher.html @@ -37,7 +37,7 @@

To open the PDB Sequence Fetcher, select PDB as the database from any Sequence Fetcher dialog (opened via - "File →Fetch Sequences"). + "File →Fetch Sequences").

PDB sequence fetcher (introduced in Jalview 2.9) @@ -45,13 +45,18 @@

Searching the PDB Database

+

To search the PDB, begin typing in the text box. If the + 'autosearch' checkbox is enabled, then the results of your query + will be automatically updated and shown in the search results tab; + otherwise, press return to update the results. To access previous + searches, press the down-arrow or click the drop down menu icon at + the side of the search box. If you just want to paste in a list of + IDs, the 'Retrieve IDs' tab provides a batch-retrieval interface.

- To search the PDB, begin typing in the text box. The results of your - query are shown in the search results tab, which updates every time - you type in the search text box. You can sort results according to - the displayed columns, and select entries with the mouse and - keyboard. Once you have selected one or more entries, hit the OK - button to retrieve and view them in Jalview. + You can sort results according to the displayed columns, and select + entries with the mouse and keyboard. Once you have selected one or + more entries, hit the OK button to retrieve and + view them in Jalview.

    @@ -64,9 +69,8 @@ 1xyz:A
  • Bulk PDB retrieval
    Multiple PDB - IDs can be specified by separating them with a semi-colon.
    - e.g. 1xyz;2xyz;3xyz
    Hitting Return or OK will automatically - fetch those IDs, like the default Sequence Fetcher interface.
  • + IDs can be specified for retrieval via the + Retrieve IDs tab.
  • Wild card searching
    The following wild cards are supported by the EMBL-EBI PDBe query service: diff --git a/help/html/features/uniprotseqfetcher.png b/help/html/features/uniprotseqfetcher.png index a592e8e..23b55fa 100644 Binary files a/help/html/features/uniprotseqfetcher.png and b/help/html/features/uniprotseqfetcher.png differ diff --git a/help/html/features/uniprotsequencefetcher.html b/help/html/features/uniprotsequencefetcher.html index edd8995..4a64f52 100644 --- a/help/html/features/uniprotsequencefetcher.html +++ b/help/html/features/uniprotsequencefetcher.html @@ -46,10 +46,13 @@

    Searching the UniProt Database

    -

    - To search UniProt, simply begin typing in the text box. After a - short delay (about 1.5 seconds), results will be shown in the table - below. You can sort results by clicking on the displayed columns, +

    To search UniProt, simply begin typing in the text box. If the + 'autosearch' check box is enabled, then after a short delay (about + 1.5 seconds), results will be shown in the table below. Results are + also updated whenever you press Enter, and you can access previous + searches by pressing the 'Down' arrow or clicking the drop-down menu + icon at the side of the search box.

    +

    You can sort results by clicking on the displayed columns, and select entries with the mouse or keyboard. Once you have selected one or more entries, hit the OK button to retrieve the sequences. @@ -61,11 +64,8 @@

  • Bulk UniProt record retrieval
    To - retrieve several uniprot accessions at once, first select UniProt - ID from the dropdown menu, then paste in the accession IDs as a - semi-colon separated list. (e.g. fila_human; mnt_human; - mnt_mouse).
    Hitting Return or OK will automatically fetch - those IDs, like the default Sequence Fetcher interface.
  • + retrieve sequences for a list of Uniprot accessions, please enter + them via the 'Retrieve IDs' tab.
  • Complex queries with the UniProt query Syntax The text box also allows complex diff --git a/help/html/releases.html b/help/html/releases.html index 8e55ca1..6396313 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -71,7 +71,7 @@ li:before {
    2.10.3
    - 10/10/2017
    + 14/11/2017
    @@ -81,14 +81,90 @@ li:before { Faster and more efficient management and rendering of sequence features
  • +
  • + More reliable Ensembl fetching with HTTP + 429 rate limit request hander +
  • +
  • + Structure views don't get updated unless + their colours have changed +
  • +
  • All linked sequences are highlighted for a structure mousover (Jmol) or selection (Chimera)
  • +
  • 'Cancel' button in progress bar for JABAWS AACon, RNAAliFold and Disorder prediction jobs +
  • + +
  • Stop codons are excluded in CDS/Protein view from Ensembl locus cross-references
  • +
  • Start/End limits are shown in Pairwise Alignment report
  • +
  • Sequence fetcher's Free text 'autosearch' feature can be disabled
  • +
  • Retrieve IDs tab added for UniProt and PDB easier retrieval of sequences for lists of IDs
  • + +
+ Scripting +
    +
  • Groovy interpreter updated to 2.4.12
  • +
  • Example groovy script for generating a matrix of percent identity scores for current alignment.
+ Testing and Deployment +
  • Test to catch memory leaks in Jalview UI
+
- + General +
    +
  • Pressing tab after updating the colour threshold text field doesn't trigger an update to the alignment view
  • +
  • Race condition when parsing sequence ID strings in parallel
  • +
  • Overview windows are also closed when alignment window is closed
  • +
  • Export of features doesn't always respect group visibility
  • +
+ Desktop
    +
  • Structures with whitespace chainCode cannot be viewed in Chimera
  • Protein annotation panel too high in CDS/Protein view
  • +
  • Can't edit the query after the server error warning icon is shown in Uniprot and PDB Free Text Search Dialogs +
  • +
  • Slow EnsemblGenome ID lookup
  • +
  • Revised Ensembl REST API CDNA query
  • +
  • Hidden column marker in last column not rendered when switching back from Wrapped to normal view
  • +
  • Annotation display corrupted when scrolling right in unwapped alignment view
  • +
  • Existing features on subsequence incorrectly relocated when full sequence retrieved from database
  • +
  • Last reported memory still shown when Desktop->Show Memory is unticked (OSX only)
  • +
  • Amend Features dialog doesn't allow features of same type and group to be selected for amending
  • +
  • Jalview becomes sluggish in wide alignments when hidden columns are present
  • +
  • Jalview freezes when loading and displaying several structures
  • +
  • Black outlines left after resizing or moving a window
  • +
  • Unable to minimise windows within the Jalview desktop on OSX
  • +
  • Mouse wheel doesn't scroll vertically when in wrapped alignment mode
  • +
  • Scale mark not shown when close to right hand end of alignment
  • +
  • Pairwise alignment only aligns selected regions of each selected sequence
  • +
  • Alignment ruler height set incorrectly after canceling the Alignment Window's Font dialog
  • +
  • Show cross-references not enabled after restoring project until a new view is created
  • +
  • Warning popup about use of SEQUENCE_ID in URL links appears when only default EMBL-EBI link is configured (since 2.10.2b2)
  • +
  • Overview redraws whole window when box position is adjusted
  • +
  • Structure viewer doesn't map all chains in a multi-chain structure when viewing alignment involving more than one chain (since 2.10)
  • +
+ Applet
+
    +
  • Concurrent modification exception when closing alignment panel
  • +
+ BioJSON
+
    +
  • + BioJSON export does not preserve non-positional features +
  • +
+ Known Java 9 Issues +
    +
  • Groovy Console very slow to open and is + not responsive when entering characters (Webstart, Java 9.01, + OSX 10.10) +
  • +
+ New Known Issues +
    +
+
@@ -1446,6 +1522,10 @@ li:before { after clicking on it to create new annotation for a column. +
  • + Null Pointer Exception raised when + pressing Add on an orphaned cut'n'paste window. +
  • diff --git a/help/html/whatsNew.html b/help/html/whatsNew.html index 3475012..9cf1044 100755 --- a/help/html/whatsNew.html +++ b/help/html/whatsNew.html @@ -27,11 +27,18 @@ What's new in Jalview 2.10.3 ?

    - Version 2.10.3 is due for release in October 2017. The full list of + Version 2.10.3 was released in November 2017. The full list of bug fixes and new features can be found in the 2.10.3 Release Notes, but the highlights are below.

    +
      +
    • Faster import and more responsive UI when working with wide alignments and handling hundreds and thousands of sequence features
    • +
    • +
    • Improved usability with PDB and + UniProt Free Text Search + dialog, and new tab for retrieval of sequences for lists of IDs.
    • +

    Experimental Features

    diff --git a/lib/groovy-all-2.4.6-indy.jar b/lib/groovy-all-2.4.12-indy.jar similarity index 62% rename from lib/groovy-all-2.4.6-indy.jar rename to lib/groovy-all-2.4.12-indy.jar index 5f3d51c..bb246a3 100644 Binary files a/lib/groovy-all-2.4.6-indy.jar and b/lib/groovy-all-2.4.12-indy.jar differ diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 5d9bdff..94f7eff 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1319,3 +1319,6 @@ label.select_hidden_colour = Select hidden colour label.overview = Overview label.reset_to_defaults = Reset to defaults label.oview_calc = Recalculating overview... +option.enable_disable_autosearch = When ticked, search is performed automatically. +option.autosearch = Autosearch +label.retrieve_ids = Retrieve IDs \ No newline at end of file diff --git a/src/MCview/AppletPDBCanvas.java b/src/MCview/AppletPDBCanvas.java index f94faba..b15c3cc 100644 --- a/src/MCview/AppletPDBCanvas.java +++ b/src/MCview/AppletPDBCanvas.java @@ -159,7 +159,7 @@ public class AppletPDBCanvas extends Panel try { - pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol); + pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol, null); if (protocol == DataSourceType.PASTE) { diff --git a/src/MCview/PDBCanvas.java b/src/MCview/PDBCanvas.java index b2f2503..ab172f2 100644 --- a/src/MCview/PDBCanvas.java +++ b/src/MCview/PDBCanvas.java @@ -153,7 +153,8 @@ public class PDBCanvas extends JPanel try { - pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol); + pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol, + ap.alignFrame); if (protocol.equals(jalview.io.DataSourceType.PASTE)) { diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java index 0045e97..3abbe75 100644 --- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java +++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java @@ -383,6 +383,8 @@ public class ChimeraResidue implements ChimeraStructuralObject, public void splitInsertionCode(String residue) { // OK, split the index into number and insertion code + // JBPNote - m.matches() can be true even if there is no resnum - this can + // cause NumberFormatExceptions below Pattern p = Pattern.compile("(\\d*)([A-Z]?)"); Matcher m = p.matcher(residue); if (m.matches()) diff --git a/src/jalview/analysis/AlignSeq.java b/src/jalview/analysis/AlignSeq.java index 34a21e6..1b2578e 100755 --- a/src/jalview/analysis/AlignSeq.java +++ b/src/jalview/analysis/AlignSeq.java @@ -36,6 +36,7 @@ import jalview.util.MessageManager; import java.awt.Color; import java.awt.Graphics; +import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -49,6 +50,14 @@ import java.util.StringTokenizer; */ public class AlignSeq { + private static final int MAX_NAME_LENGTH = 30; + + private static final int GAP_OPEN_COST = 120; + + private static final int GAP_EXTEND_COST = 20; + + private static final int GAP_INDEX = -1; + public static final String PEP = "pep"; public static final String DNA = "dna"; @@ -61,7 +70,7 @@ public class AlignSeq float[][] F; - int[][] traceback; + int[][] traceback; // todo is this actually used? int[] seq1; @@ -96,30 +105,20 @@ public class AlignSeq /** DOCUMENT ME!! */ public int seq2start; - /** DOCUMENT ME!! */ public int seq2end; int count; - /** DOCUMENT ME!! */ public float maxscore; - float pid; - int prev = 0; - int gapOpen = 120; - - int gapExtend = 20; - StringBuffer output = new StringBuffer(); String type; // AlignSeq.PEP or AlignSeq.DNA private ScoreMatrix scoreMatrix; - private static final int GAP_INDEX = -1; - /** * Creates a new AlignSeq object. * @@ -378,11 +377,10 @@ public class AlignSeq } } - // System.out.println(maxi + " " + maxj + " " + score[maxi][maxj]); int i = maxi; int j = maxj; int trace; - maxscore = score[i][j] / 10; + maxscore = score[i][j] / 10f; seq1end = maxi + 1; seq2end = maxj + 1; @@ -451,49 +449,48 @@ public class AlignSeq /** * DOCUMENT ME! */ - public void printAlignment(java.io.PrintStream os) + public void printAlignment(PrintStream os) { // TODO: Use original sequence characters rather than re-translated // characters in output // Find the biggest id length for formatting purposes - String s1id = s1.getName(), s2id = s2.getName(); - int maxid = s1.getName().length(); - if (s2.getName().length() > maxid) - { - maxid = s2.getName().length(); - } - if (maxid > 30) + String s1id = getAlignedSeq1().getDisplayId(true); + String s2id = getAlignedSeq2().getDisplayId(true); + int nameLength = Math.max(s1id.length(), s2id.length()); + if (nameLength > MAX_NAME_LENGTH) { - maxid = 30; + int truncateBy = nameLength - MAX_NAME_LENGTH; + nameLength = MAX_NAME_LENGTH; // JAL-527 - truncate the sequence ids - if (s1.getName().length() > maxid) + if (s1id.length() > nameLength) { - s1id = s1.getName().substring(0, 30); + int slashPos = s1id.lastIndexOf('/'); + s1id = s1id.substring(0, slashPos - truncateBy) + + s1id.substring(slashPos); } - if (s2.getName().length() > maxid) + if (s2id.length() > nameLength) { - s2id = s2.getName().substring(0, 30); + int slashPos = s2id.lastIndexOf('/'); + s2id = s2id.substring(0, slashPos - truncateBy) + + s2id.substring(slashPos); } } - int len = 72 - maxid - 1; + int len = 72 - nameLength - 1; int nochunks = ((aseq1.length - count) / len) + ((aseq1.length - count) % len > 0 ? 1 : 0); - pid = 0; + float pid = 0f; output.append("Score = ").append(score[maxi][maxj]).append(NEWLINE); output.append("Length of alignment = ") .append(String.valueOf(aseq1.length - count)).append(NEWLINE); output.append("Sequence "); - output.append(new Format("%" + maxid + "s").form(s1.getName())); - output.append(" : ").append(String.valueOf(s1.getStart())) - .append(" - ").append(String.valueOf(s1.getEnd())); + Format nameFormat = new Format("%" + nameLength + "s"); + output.append(nameFormat.form(s1id)); output.append(" (Sequence length = ") .append(String.valueOf(s1str.length())).append(")") .append(NEWLINE); output.append("Sequence "); - output.append(new Format("%" + maxid + "s").form(s2.getName())); - output.append(" : ").append(String.valueOf(s2.getStart())) - .append(" - ").append(String.valueOf(s2.getEnd())); + output.append(nameFormat.form(s2id)); output.append(" (Sequence length = ") .append(String.valueOf(s2str.length())).append(")") .append(NEWLINE).append(NEWLINE); @@ -503,7 +500,7 @@ public class AlignSeq for (int j = 0; j < nochunks; j++) { // Print the first aligned sequence - output.append(new Format("%" + (maxid) + "s").form(s1id)).append(" "); + output.append(nameFormat.form(s1id)).append(" "); for (int i = 0; i < len; i++) { @@ -514,7 +511,7 @@ public class AlignSeq } output.append(NEWLINE); - output.append(new Format("%" + (maxid) + "s").form(" ")).append(" "); + output.append(nameFormat.form(" ")).append(" "); /* * Print out the match symbols: @@ -534,7 +531,7 @@ public class AlignSeq pid++; output.append("|"); } - else if (type.equals("pep")) + else if (PEP.equals(type)) { if (pam250.getPairwiseScore(c1, c2) > 0) { @@ -554,8 +551,7 @@ public class AlignSeq // Now print the second aligned sequence output = output.append(NEWLINE); - output = output.append(new Format("%" + (maxid) + "s").form(s2id)) - .append(" "); + output = output.append(nameFormat.form(s2id)).append(" "); for (int i = 0; i < len; i++) { @@ -569,7 +565,8 @@ public class AlignSeq } pid = pid / (aseq1.length - count) * 100; - output = output.append(new Format("Percentage ID = %2.2f\n").form(pid)); + output.append(new Format("Percentage ID = %3.2f\n").form(pid)); + output.append(NEWLINE); try { os.print(output.toString()); @@ -591,7 +588,6 @@ public class AlignSeq public int findTrace(int i, int j) { int t = 0; - // float pairwiseScore = lookup[seq1[i]][seq2[j]]; float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(j)); float max = score[i - 1][j - 1] + (pairwiseScore * 10); @@ -640,19 +636,19 @@ public class AlignSeq // top left hand element score[0][0] = scoreMatrix.getPairwiseScore(s1str.charAt(0), s2str.charAt(0)) * 10; - E[0][0] = -gapExtend; + E[0][0] = -GAP_EXTEND_COST; F[0][0] = 0; // Calculate the top row first for (int j = 1; j < m; j++) { // What should these values be? 0 maybe - E[0][j] = max(score[0][j - 1] - gapOpen, E[0][j - 1] - gapExtend); - F[0][j] = -gapExtend; + E[0][j] = max(score[0][j - 1] - GAP_OPEN_COST, E[0][j - 1] - GAP_EXTEND_COST); + F[0][j] = -GAP_EXTEND_COST; float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(0), s2str.charAt(j)); - score[0][j] = max(pairwiseScore * 10, -gapOpen, -gapExtend); + score[0][j] = max(pairwiseScore * 10, -GAP_OPEN_COST, -GAP_EXTEND_COST); traceback[0][j] = 1; } @@ -660,8 +656,8 @@ public class AlignSeq // Now do the left hand column for (int i = 1; i < n; i++) { - E[i][0] = -gapOpen; - F[i][0] = max(score[i - 1][0] - gapOpen, F[i - 1][0] - gapExtend); + E[i][0] = -GAP_OPEN_COST; + F[i][0] = max(score[i - 1][0] - GAP_OPEN_COST, F[i - 1][0] - GAP_EXTEND_COST); float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(0)); @@ -674,8 +670,8 @@ public class AlignSeq { for (int j = 1; j < m; j++) { - E[i][j] = max(score[i][j - 1] - gapOpen, E[i][j - 1] - gapExtend); - F[i][j] = max(score[i - 1][j] - gapOpen, F[i - 1][j] - gapExtend); + E[i][j] = max(score[i][j - 1] - GAP_OPEN_COST, E[i][j - 1] - GAP_EXTEND_COST); + F[i][j] = max(score[i - 1][j] - GAP_OPEN_COST, F[i - 1][j] - GAP_EXTEND_COST); float pairwiseScore = scoreMatrix.getPairwiseScore(s1str.charAt(i), s2str.charAt(j)); diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java index 2b9b9f9..90d9197 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -2164,7 +2164,10 @@ public class AlignmentUtils /** * Returns a mapping from dna to protein by inspecting sequence features of - * type "CDS" on the dna. + * type "CDS" on the dna. A mapping is constructed if the total CDS feature + * length is 3 times the peptide length (optionally after dropping a trailing + * stop codon). This method does not check whether the CDS nucleotide sequence + * translates to the peptide sequence. * * @param dnaSeq * @param proteinSeq @@ -2176,6 +2179,15 @@ public class AlignmentUtils List ranges = findCdsPositions(dnaSeq); int mappedDnaLength = MappingUtils.getLength(ranges); + /* + * if not a whole number of codons, something is wrong, + * abort mapping + */ + if (mappedDnaLength % CODON_LENGTH > 0) + { + return null; + } + int proteinLength = proteinSeq.getLength(); int proteinStart = proteinSeq.getStart(); int proteinEnd = proteinSeq.getEnd(); @@ -2199,8 +2211,12 @@ public class AlignmentUtils if (codesForResidues == (proteinLength + 1)) { // assuming extra codon is for STOP and not in peptide + // todo: check trailing codon is indeed a STOP codon codesForResidues--; + mappedDnaLength -= CODON_LENGTH; + MappingUtils.removeEndPositions(CODON_LENGTH, ranges); } + if (codesForResidues == proteinLength) { proteinRange.add(new int[] { proteinStart, proteinEnd }); @@ -2211,7 +2227,7 @@ public class AlignmentUtils /** * Returns a list of CDS ranges found (as sequence positions base 1), i.e. of - * start/end positions of sequence features of type "CDS" (or a sub-type of + * [start, end] positions of sequence features of type "CDS" (or a sub-type of * CDS in the Sequence Ontology). The ranges are sorted into ascending start * position order, so this method is only valid for linear CDS in the same * sense as the protein product. @@ -2230,7 +2246,6 @@ public class AlignmentUtils return result; } SequenceFeatures.sortFeatures(sfs, true); - int startPhase = 0; for (SequenceFeature sf : sfs) { @@ -2248,7 +2263,7 @@ public class AlignmentUtils */ int begin = sf.getBegin(); int end = sf.getEnd(); - if (result.isEmpty()) + if (result.isEmpty() && phase > 0) { begin += phase; if (begin > end) @@ -2263,16 +2278,6 @@ public class AlignmentUtils } /* - * remove 'startPhase' positions (usually 0) from the first range - * so we begin at the start of a complete codon - */ - if (!result.isEmpty()) - { - // TODO JAL-2022 correctly model start phase > 0 - result.get(0)[0] += startPhase; - } - - /* * Finally sort ranges by start position. This avoids a dependency on * keeping features in order on the sequence (if they are in order anyway, * the sort will have almost no work to do). The implicit assumption is CDS diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 9e6d1c0..931eba6 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -21,10 +21,10 @@ package jalview.api; import jalview.analysis.Conservation; +import jalview.analysis.TreeModel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; -import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; import jalview.datamodel.ProfilesI; import jalview.datamodel.SearchResultsI; @@ -243,16 +243,6 @@ public interface AlignViewportI extends ViewStyleI void clearSequenceColours(); /** - * This method returns the visible alignment as text, as seen on the GUI, ie - * if columns are hidden they will not be returned in the result. Use this for - * calculating trees, PCA, redundancy etc on views which contain hidden - * columns. - * - * @return String[] - */ - CigarArray getViewAsCigars(boolean selectedRegionOnly); - - /** * return a compact representation of the current alignment selection to pass * to an analysis function * @@ -486,6 +476,7 @@ public interface AlignViewportI extends ViewStyleI * * @return */ + @Override boolean isProteinFontAsCdna(); /** @@ -493,5 +484,10 @@ public interface AlignViewportI extends ViewStyleI * * @return */ + @Override void setProteinFontAsCdna(boolean b); + + public abstract TreeModel getCurrentTree(); + + public abstract void setCurrentTree(TreeModel tree); } diff --git a/src/jalview/api/AlignmentViewPanel.java b/src/jalview/api/AlignmentViewPanel.java index ef59996..0b1ca21 100644 --- a/src/jalview/api/AlignmentViewPanel.java +++ b/src/jalview/api/AlignmentViewPanel.java @@ -43,9 +43,10 @@ public interface AlignmentViewPanel extends OOMHandlerI * * @param updateOverview * - if true, the overview panel will also be updated and repainted + * @param updateStructures + * - if true then any linked structure views will also be updated */ - - void paintAlignment(boolean updateOverview); + void paintAlignment(boolean updateOverview, boolean updateStructures); /** * automatically adjust annotation panel height for new annotation whilst diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index 9d44479..46bd4fd 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -383,7 +383,7 @@ public class APopupMenu extends java.awt.PopupMenu void addFeatureLinks(final SequenceI seq, List links) { Menu linkMenu = new Menu(MessageManager.getString("action.link")); - Map> linkset = new LinkedHashMap>(); + Map> linkset = new LinkedHashMap<>(); for (String link : links) { @@ -485,8 +485,8 @@ public class APopupMenu extends java.awt.PopupMenu * Temporary store to hold distinct calcId / type pairs for the tooltip. * Using TreeMap means calcIds are shown in alphabetical order. */ - SortedMap tipEntries = new TreeMap(); - final Map> candidates = new LinkedHashMap>(); + SortedMap tipEntries = new TreeMap<>(); + final Map> candidates = new LinkedHashMap<>(); AlignmentI al = this.ap.av.getAlignment(); AlignmentUtils.findAddableReferenceAnnotations(forSequences, tipEntries, candidates, al); @@ -825,8 +825,8 @@ public class APopupMenu extends java.awt.PopupMenu } int gSize = sg.getSize(); - List seqs = new ArrayList(); - List features = new ArrayList(); + List seqs = new ArrayList<>(); + List features = new ArrayList<>(); for (int i = 0; i < gSize; i++) { @@ -930,7 +930,7 @@ public class APopupMenu extends java.awt.PopupMenu { seq.setName(dialog.getName()); seq.setDescription(dialog.getDescription()); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -1163,7 +1163,7 @@ public class APopupMenu extends java.awt.PopupMenu void refresh() { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } protected void clustalColour_actionPerformed() @@ -1339,7 +1339,7 @@ public class APopupMenu extends java.awt.PopupMenu SequenceGroup sg = ap.av.getSelectionGroup(); ap.av.getAlignment().deleteGroup(sg); ap.av.setSelectionGroup(null); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } void createGroupMenuItem_actionPerformed() @@ -1440,8 +1440,8 @@ public class APopupMenu extends java.awt.PopupMenu * the insertion order, which is the order of the annotations on the * alignment. */ - Map>> shownTypes = new LinkedHashMap>>(); - Map>> hiddenTypes = new LinkedHashMap>>(); + Map>> shownTypes = new LinkedHashMap<>(); + Map>> hiddenTypes = new LinkedHashMap<>(); AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes, AlignmentAnnotationUtils.asList(annotations), forSequences); diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index ed04a0a..ef87671 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -343,7 +343,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, createAlignFrameWindow(embedded); validate(); alignPanel.adjustAnnotationHeight(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } public AlignViewport getAlignViewport() @@ -415,7 +415,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { viewport.featureSettings.refreshTable(); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); statusBar.setText(MessageManager .getString("label.successfully_added_features_alignment")); } @@ -691,7 +691,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, break; } - alignPanel.paintAlignment(true); + // TODO: repaint flags set only if the keystroke warrants it + alignPanel.paintAlignment(true, true); } /** @@ -917,7 +918,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { applyAutoAnnotationSettings_actionPerformed(); } - alignPanel.paintAlignment(true); + // TODO: repaint flags set only if warranted + alignPanel.paintAlignment(true, true); } /** @@ -1094,7 +1096,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, else if (source == invertColSel) { viewport.invertColumnSelection(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); viewport.sendSelection(); } else if (source == remove2LeftMenuItem) @@ -1128,34 +1130,34 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, else if (source == showColumns) { viewport.showAllHiddenColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == showSeqs) { viewport.showAllHiddenSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); // uncomment if we want to slave sequence selections in split frame // viewport.sendSelection(); } else if (source == hideColumns) { viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == hideSequences && viewport.getSelectionGroup() != null) { viewport.hideAllSelectedSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); // uncomment if we want to slave sequence selections in split frame // viewport.sendSelection(); } else if (source == hideAllButSelection) { toggleHiddenRegions(false, false); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == hideAllSelection) @@ -1164,14 +1166,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport.expandColSelection(sg, false); viewport.hideAllSelectedSeqs(); viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == showAllHidden) { viewport.showAllHiddenColumns(); viewport.showAllHiddenSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == showGroupConsensus) @@ -1601,10 +1603,12 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { System.exit(0); } - else + + viewport = null; + if (alignPanel != null && alignPanel.overviewPanel != null) { + alignPanel.overviewPanel.dispose(); } - viewport = null; alignPanel = null; this.dispose(); } @@ -1781,7 +1785,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } viewport.getAlignment().moveSelectedSequencesByOne(sg, up ? null : viewport.getHiddenRepSequences(), up); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); /* * Also move cDNA/protein complement sequences @@ -1793,7 +1797,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport, complement); complement.getAlignment().moveSelectedSequencesByOne(mappedSelection, up ? null : complement.getHiddenRepSequences(), up); - getSplitFrame().getComplement(this).alignPanel.paintAlignment(true); + getSplitFrame().getComplement(this).alignPanel.paintAlignment(true, + false); } } @@ -2226,7 +2231,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -2263,7 +2268,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2283,7 +2288,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2303,7 +2308,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, public void invertColSel_actionPerformed() { viewport.invertColumnSelection(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2595,19 +2600,19 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { viewport.setShowJVSuffix(seqLimits.getState()); alignPanel.fontChanged(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } protected void colourTextMenuItem_actionPerformed() { viewport.setColourText(colourTextMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } protected void displayNonconservedMenuItem_actionPerformed() { viewport.setShowUnconserved(displayNonconservedMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } protected void wrapMenuItem_actionPerformed() @@ -2617,7 +2622,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, scaleAbove.setEnabled(wrapMenuItem.getState()); scaleLeft.setEnabled(wrapMenuItem.getState()); scaleRight.setEnabled(wrapMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void overviewMenuItem_actionPerformed() @@ -2660,7 +2665,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { viewport.setGlobalColourScheme(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } protected void modifyPID_actionPerformed() @@ -2735,7 +2740,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void sortIDMenuItem_actionPerformed() @@ -2744,7 +2749,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, AlignmentSorter.sortByID(viewport.getAlignment()); addHistoryItem( new OrderCommand("ID Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void sortLengthMenuItem_actionPerformed() @@ -2753,7 +2758,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, AlignmentSorter.sortByLength(viewport.getAlignment()); addHistoryItem(new OrderCommand("Length Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void sortGroupMenuItem_actionPerformed() @@ -2762,7 +2767,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, AlignmentSorter.sortByGroup(viewport.getAlignment()); addHistoryItem(new OrderCommand("Group Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } @@ -2802,7 +2807,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, current.insertCharAt(Width - 1, viewport.getGapCharacter()); } } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } if ((viewport.getSelectionGroup() != null @@ -2866,7 +2871,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, current.insertCharAt(Width - 1, viewport.getGapCharacter()); } } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } @@ -2919,7 +2924,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, addHistoryItem(new OrderCommand(MessageManager .formatMessage("label.order_by_params", new String[] { title }), oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -2977,7 +2982,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, addHistoryItem(new OrderCommand(undoname, oldOrder, viewport.getAlignment())); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); return true; } @@ -4144,7 +4149,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { // register the association(s) and quit, don't create any windows. if (StructureSelectionManager.getStructureSelectionManager(applet) - .setMapping(seqs, chains, pdb.getFile(), protocol) == null) + .setMapping(seqs, chains, pdb.getFile(), protocol, null) == null) { System.err.println("Failed to map " + pdb.getFile() + " (" + protocol + ") to any sequences"); diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index b07666e..262948d 100644 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -20,7 +20,6 @@ */ package jalview.appletgui; -import jalview.analysis.TreeModel; import jalview.api.AlignViewportI; import jalview.api.FeatureSettingsModelI; import jalview.bin.JalviewLite; @@ -54,23 +53,12 @@ public class AlignViewport extends AlignmentViewport boolean validCharWidth = true; - TreeModel currentTree = null; - public jalview.bin.JalviewLite applet; boolean MAC = false; private AnnotationColumnChooser annotationColumnSelectionState; - @Override - public void finalize() - { - applet = null; - quality = null; - alignment = null; - colSel = null; - } - public AlignViewport(AlignmentI al, JalviewLite applet) { super(al); @@ -274,16 +262,6 @@ public class AlignViewport extends AlignmentViewport ranges.setEndSeq(height / getCharHeight()); } - public void setCurrentTree(TreeModel tree) - { - currentTree = tree; - } - - public TreeModel getCurrentTree() - { - return currentTree; - } - boolean centreColumnLabels; public boolean getCentreColumnLabels() diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index 8e333ba..270b2f7 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -73,23 +73,6 @@ public class AlignmentPanel extends Panel // this value is set false when selection area being dragged boolean fastPaint = true; - @Override - public void finalize() throws Throwable - { - alignFrame = null; - av = null; - vpRanges = null; - seqPanel = null; - seqPanelHolder = null; - sequenceHolderPanel = null; - scalePanel = null; - scalePanelHolder = null; - annotationPanel = null; - annotationPanelHolder = null; - annotationSpaceFillerHolder = null; - super.finalize(); - } - public AlignmentPanel(AlignFrame af, final AlignViewport av) { try @@ -530,7 +513,7 @@ public class AlignmentPanel extends Panel vpRanges.scrollToWrappedVisible(start); } - paintAlignment(redrawOverview); + paintAlignment(redrawOverview, false); return true; } @@ -579,7 +562,7 @@ public class AlignmentPanel extends Panel apvscroll.addNotify(); hscroll.addNotify(); validate(); - paintAlignment(true); + paintAlignment(true, false); } /** @@ -930,7 +913,8 @@ public class AlignmentPanel extends Panel * Repaint the alignment and annotations, and, optionally, any overview window */ @Override - public void paintAlignment(boolean updateOverview) + public void paintAlignment(boolean updateOverview, + boolean updateStructures) { final AnnotationSorter sorter = new AnnotationSorter(getAlignment(), av.isShowAutocalculatedAbove()); @@ -938,13 +922,14 @@ public class AlignmentPanel extends Panel av.getSortAnnotationsBy()); repaint(); - if (updateOverview) + if (updateStructures) { - // TODO: determine if this paintAlignment changed structure colours jalview.structure.StructureSelectionManager .getStructureSelectionManager(av.applet) .sequenceColoursChanged(this); - + } + if (updateOverview) + { if (overviewPanel != null) { overviewPanel.updateOverviewImage(); diff --git a/src/jalview/appletgui/AnnotationColourChooser.java b/src/jalview/appletgui/AnnotationColourChooser.java index 8de751a..533226e 100644 --- a/src/jalview/appletgui/AnnotationColourChooser.java +++ b/src/jalview/appletgui/AnnotationColourChooser.java @@ -85,7 +85,7 @@ public class AnnotationColourChooser extends Panel implements oldcs = av.getGlobalColourScheme(); if (av.getAlignment().getGroups() != null) { - oldgroupColours = new HashMap(); + oldgroupColours = new HashMap<>(); for (SequenceGroup sg : ap.av.getAlignment().getGroups()) { oldgroupColours.put(sg, sg.getColourScheme()); @@ -180,8 +180,8 @@ public class AnnotationColourChooser extends Panel implements // TODO remove duplication with gui.AnnotationRowFilter // TODO add 'per sequence only' option / parameter - annotationLabels = new HashMap(); - Vector list = new Vector(); + annotationLabels = new HashMap<>(); + Vector list = new Vector<>(); AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation(); if (anns == null) { @@ -376,7 +376,7 @@ public class AnnotationColourChooser extends Panel implements else if (evt.getSource() == cancel) { reset(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); frame.setVisible(false); } @@ -417,7 +417,7 @@ public class AnnotationColourChooser extends Panel implements } currentAnnotation.threshold.value = slider.getValue() / 1000f; - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -559,7 +559,7 @@ public class AnnotationColourChooser extends Panel implements // update colours in linked windows ap.alignmentChanged(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } void reset() @@ -572,7 +572,7 @@ public class AnnotationColourChooser extends Panel implements sg.setColourScheme(oldgroupColours.get(sg)); } } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override @@ -588,7 +588,7 @@ public class AnnotationColourChooser extends Panel implements @Override public void mouseReleased(MouseEvent evt) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override diff --git a/src/jalview/appletgui/AnnotationColumnChooser.java b/src/jalview/appletgui/AnnotationColumnChooser.java index b4c1d54..7674de716 100644 --- a/src/jalview/appletgui/AnnotationColumnChooser.java +++ b/src/jalview/appletgui/AnnotationColumnChooser.java @@ -306,7 +306,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements av.getAlignment().setHiddenColumns(oldHidden); } av.sendSelection(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } @@ -348,7 +348,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements sliderDragging = false; valueChanged(true); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } }); } @@ -359,8 +359,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements if (slider.isEnabled()) { getCurrentAnnotation().threshold.value = slider.getValue() / 1000f; - updateView(); - ap.paintAlignment(false); + updateView(); // this also calls paintAlignment(true,true) } } @@ -515,7 +514,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements filterParams = null; av.setAnnotationColumnSelectionState(this); av.sendSelection(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } public HiddenColumns getOldHiddenColumns() diff --git a/src/jalview/appletgui/AnnotationLabels.java b/src/jalview/appletgui/AnnotationLabels.java index 2fb737a..d8f65a5 100755 --- a/src/jalview/appletgui/AnnotationLabels.java +++ b/src/jalview/appletgui/AnnotationLabels.java @@ -226,7 +226,8 @@ public class AnnotationLabels extends Panel ap.annotationPanel.adjustPanelHeight(); setSize(getSize().width, ap.annotationPanel.getSize().height); ap.validate(); - ap.paintAlignment(true); + // TODO: only paint if we needed to + ap.paintAlignment(true, true); } boolean editLabelDescription(AlignmentAnnotation annotation) @@ -548,7 +549,7 @@ public class AnnotationLabels extends Panel { ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } }); popup.add(cbmi); @@ -756,7 +757,7 @@ public class AnnotationLabels extends Panel } } } - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } @@ -813,7 +814,7 @@ public class AnnotationLabels extends Panel sg.addSequence(aa[selectedRow].sequenceRef, false); } ap.av.setSelectionGroup(sg); - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java index 6fe71de..c06f7b1 100755 --- a/src/jalview/appletgui/AnnotationPanel.java +++ b/src/jalview/appletgui/AnnotationPanel.java @@ -439,7 +439,8 @@ public class AnnotationPanel extends Panel graphStretchY = evt.getY(); av.calcPanelHeight(); needValidating = true; - ap.paintAlignment(true); + // TODO: only update overview visible geometry + ap.paintAlignment(true, false); } else { diff --git a/src/jalview/appletgui/AnnotationRowFilter.java b/src/jalview/appletgui/AnnotationRowFilter.java index 5efd177..c96dbab 100644 --- a/src/jalview/appletgui/AnnotationRowFilter.java +++ b/src/jalview/appletgui/AnnotationRowFilter.java @@ -114,7 +114,7 @@ public abstract class AnnotationRowFilter extends Panel public void cancel_actionPerformed(ActionEvent e) { reset(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); frame.setVisible(false); } diff --git a/src/jalview/appletgui/AppletJmol.java b/src/jalview/appletgui/AppletJmol.java index 49219b9..3d1442d 100644 --- a/src/jalview/appletgui/AppletJmol.java +++ b/src/jalview/appletgui/AppletJmol.java @@ -134,7 +134,7 @@ public class AppletJmol extends EmbmenuFrame implements AlignmentPanel ap; - List _aps = new ArrayList(); // remove? never + List _aps = new ArrayList<>(); // remove? never // added to String fileLoadingError; @@ -213,7 +213,7 @@ public class AppletJmol extends EmbmenuFrame implements { reader = StructureSelectionManager .getStructureSelectionManager(ap.av.applet) - .setMapping(seq, chains, pdbentry.getFile(), protocol); + .setMapping(seq, chains, pdbentry.getFile(), protocol, null); // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW? // FOR NOW, LETS JUST OPEN A NEW WINDOW } @@ -394,7 +394,7 @@ public class AppletJmol extends EmbmenuFrame implements void centerViewer() { - Vector toshow = new Vector(); + Vector toshow = new Vector<>(); for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof CheckboxMenuItem) diff --git a/src/jalview/appletgui/AppletJmolBinding.java b/src/jalview/appletgui/AppletJmolBinding.java index d5d53fb..2f61b24 100644 --- a/src/jalview/appletgui/AppletJmolBinding.java +++ b/src/jalview/appletgui/AppletJmolBinding.java @@ -24,6 +24,7 @@ import jalview.api.AlignmentViewPanel; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.jmol.JalviewJmolBinding; +import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import jalview.structure.StructureSelectionManager; @@ -183,4 +184,11 @@ class AppletJmolBinding extends JalviewJmolBinding // TODO Auto-generated method stub return null; } + + @Override + protected IProgressIndicator getIProgressIndicator() + { + // no progress indicators on the applet + return null; + } } diff --git a/src/jalview/appletgui/ExtJmol.java b/src/jalview/appletgui/ExtJmol.java index 3966536..89228d5 100644 --- a/src/jalview/appletgui/ExtJmol.java +++ b/src/jalview/appletgui/ExtJmol.java @@ -26,6 +26,7 @@ import jalview.api.SequenceRenderer; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.jmol.JalviewJmolBinding; +import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import java.awt.Container; @@ -65,6 +66,13 @@ public class ExtJmol extends JalviewJmolBinding } @Override + protected IProgressIndicator getIProgressIndicator() + { + // no progress indicators on applet (could access javascript for this) + return null; + } + + @Override public void updateColours(Object source) { @@ -92,6 +100,7 @@ public class ExtJmol extends JalviewJmolBinding } } + @Override public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment) { @@ -137,8 +146,8 @@ public class ExtJmol extends JalviewJmolBinding @Override public void refreshPdbEntries() { - List pdbe = new ArrayList(); - List fileids = new ArrayList(); + List pdbe = new ArrayList<>(); + List fileids = new ArrayList<>(); SequenceI[] sq = ap.av.getAlignment().getSequencesArray(); for (int s = 0; s < sq.length; s++) { diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index b35c079..df407d6 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -493,7 +493,7 @@ public class FeatureRenderer } // findAllFeatures(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); return true; } diff --git a/src/jalview/appletgui/FeatureSettings.java b/src/jalview/appletgui/FeatureSettings.java index 20d4d74..9a67499 100755 --- a/src/jalview/appletgui/FeatureSettings.java +++ b/src/jalview/appletgui/FeatureSettings.java @@ -377,8 +377,8 @@ public class FeatureSettings extends Panel // Group selection states void resetTable(boolean groupsChanged) { - List displayableTypes = new ArrayList(); - Set foundGroups = new HashSet(); + List displayableTypes = new ArrayList<>(); + Set foundGroups = new HashSet<>(); AlignmentI alignment = av.getAlignment(); @@ -391,7 +391,7 @@ public class FeatureSettings extends Panel * and keep track of which groups are visible */ Set groups = seq.getFeatures().getFeatureGroups(true); - Set visibleGroups = new HashSet(); + Set visibleGroups = new HashSet<>(); for (String group : groups) { // if (group == null || fr.checkGroupVisibility(group, true)) @@ -600,7 +600,7 @@ public class FeatureSettings extends Panel fr.setFeaturePriority(data); - ap.paintAlignment(updateOverview); + ap.paintAlignment(updateOverview, updateOverview); } MyCheckbox selectedCheck; @@ -680,7 +680,7 @@ public class FeatureSettings extends Panel { featurePanel.removeAll(); resetTable(false); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override @@ -732,7 +732,7 @@ public class FeatureSettings extends Panel public void adjustmentValueChanged(AdjustmentEvent evt) { fr.setTransparency((100 - transparency.getValue()) / 100f); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } class MyCheckbox extends Checkbox diff --git a/src/jalview/appletgui/FontChooser.java b/src/jalview/appletgui/FontChooser.java index c9a92b2..443ebce 100644 --- a/src/jalview/appletgui/FontChooser.java +++ b/src/jalview/appletgui/FontChooser.java @@ -261,7 +261,7 @@ public class FontChooser extends Panel implements ItemListener { ap.av.setCharWidth(oldCharWidth); } - ap.paintAlignment(true); + ap.paintAlignment(true, false); } else if (tp != null) { diff --git a/src/jalview/appletgui/IdPanel.java b/src/jalview/appletgui/IdPanel.java index 7d9d278..15e269c 100755 --- a/src/jalview/appletgui/IdPanel.java +++ b/src/jalview/appletgui/IdPanel.java @@ -70,7 +70,7 @@ public class IdPanel extends Panel // TODO: add in group link parameter // make a list of label,url pairs - HashMap urlList = new HashMap(); + HashMap urlList = new HashMap<>(); if (viewport.applet != null) { for (int i = 1; i < 10; i++) @@ -198,7 +198,7 @@ public class IdPanel extends Panel } lastid = seq; - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } @Override @@ -295,7 +295,7 @@ public class IdPanel extends Panel } else { - nlinks = new ArrayList(); + nlinks = new ArrayList<>(); } for (SequenceFeature sf : sq.getFeatures().getNonPositionalFeatures()) @@ -333,7 +333,7 @@ public class IdPanel extends Panel selectSeq(seq); } - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } void selectSeq(int seq) @@ -459,7 +459,7 @@ public class IdPanel extends Panel running = false; } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); try { Thread.sleep(100); diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index e74e1cd..8ce597d 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -31,6 +31,7 @@ import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.Cursor; import java.awt.Dimension; +import java.awt.Frame; import java.awt.Panel; import java.awt.PopupMenu; import java.awt.event.ComponentAdapter; @@ -200,7 +201,7 @@ public class OverviewPanel extends Panel implements Runnable, av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -322,6 +323,9 @@ public class OverviewPanel extends Panel implements Runnable, try { av.getRanges().removePropertyChangeListener(this); + Frame parent = (Frame) getParent(); + parent.dispose(); + parent.setVisible(false); } finally { av = null; diff --git a/src/jalview/appletgui/PaintRefresher.java b/src/jalview/appletgui/PaintRefresher.java index 32507fe..fe99187 100755 --- a/src/jalview/appletgui/PaintRefresher.java +++ b/src/jalview/appletgui/PaintRefresher.java @@ -24,8 +24,8 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import java.awt.Component; -import java.util.Enumeration; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; @@ -78,13 +78,14 @@ public class PaintRefresher return; } - for (String id : components.keySet()) + Iterator it = components.keySet().iterator(); + while (it.hasNext()) { - Vector comps = components.get(id); + Vector comps = components.get(it.next()); comps.removeElement(comp); - if (comps.size() == 0) + if (comps.isEmpty()) { - components.remove(id); + it.remove(); } } } @@ -110,10 +111,10 @@ public class PaintRefresher return; } - Enumeration e = comps.elements(); - while (e.hasMoreElements()) + Iterator it = comps.iterator(); + while (it.hasNext()) { - comp = e.nextElement(); + comp = it.next(); if (comp == source) { @@ -122,7 +123,7 @@ public class PaintRefresher if (!comp.isValid()) { - comps.removeElement(comp); + it.remove(); } else if (validateSequences && comp instanceof AlignmentPanel && source instanceof AlignmentPanel) diff --git a/src/jalview/appletgui/RedundancyPanel.java b/src/jalview/appletgui/RedundancyPanel.java index 2aba20c..bd36b0d 100644 --- a/src/jalview/appletgui/RedundancyPanel.java +++ b/src/jalview/appletgui/RedundancyPanel.java @@ -160,7 +160,7 @@ public class RedundancyPanel extends SliderPanel float value = slider.getValue(); - List redundantSequences = new ArrayList(); + List redundantSequences = new ArrayList<>(); for (int i = 0; i < redundancy.length; i++) { if (value <= redundancy[i]) @@ -247,7 +247,7 @@ public class RedundancyPanel extends SliderPanel ap.av.getAlignment().getSequences()); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); if (historyList.size() == 0) { diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java index 514c3f9..7d4150d 100755 --- a/src/jalview/appletgui/ScalePanel.java +++ b/src/jalview/appletgui/ScalePanel.java @@ -141,7 +141,7 @@ public class ScalePanel extends Panel sg.setStartRes(min); sg.setEndRes(max); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -167,7 +167,7 @@ public class ScalePanel extends Panel { av.showColumn(reveal[0]); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -183,7 +183,7 @@ public class ScalePanel extends Panel { av.showAllHiddenColumns(); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -208,7 +208,7 @@ public class ScalePanel extends Panel av.setSelectionGroup(null); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -239,7 +239,7 @@ public class ScalePanel extends Panel if (!stretchingGroup) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); return; } @@ -256,7 +256,7 @@ public class ScalePanel extends Panel } stretchingGroup = false; - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -285,7 +285,7 @@ public class ScalePanel extends Panel { stretchingGroup = true; cs.stretchGroup(res, sg, min, max); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index f36a8e2..9a61f5f 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -335,7 +335,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, sg.addSequence(sequence, false); av.setSelectionGroup(sg); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -417,7 +417,6 @@ public class SeqPanel extends Panel implements MouseMotionListener, * alignment column * @param seq * index of sequence in alignment - * @return position of column in sequence or -1 if at gap */ void setStatusMessage(SequenceI sequence, int column, int seq) { @@ -983,7 +982,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, lastMousePress = evt.getPoint(); - ap.paintAlignment(false); + ap.paintAlignment(false, false); ap.annotationPanel.image = null; return; } @@ -1450,7 +1449,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, { if (links == null) { - links = new Vector(); + links = new Vector<>(); } links.addAll(sf.links); } @@ -1524,7 +1523,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, } } PaintRefresher.Refresh(ap, av.getSequenceSetId()); - ap.paintAlignment(needOverviewUpdate); + ap.paintAlignment(needOverviewUpdate, needOverviewUpdate); needOverviewUpdate = false; changeEndRes = false; changeStartRes = false; diff --git a/src/jalview/appletgui/SliderPanel.java b/src/jalview/appletgui/SliderPanel.java index 565ebe8..5841e80 100644 --- a/src/jalview/appletgui/SliderPanel.java +++ b/src/jalview/appletgui/SliderPanel.java @@ -441,7 +441,7 @@ public class SliderPanel extends Panel @Override public void mouseReleased(MouseEvent evt) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override diff --git a/src/jalview/appletgui/SplitFrame.java b/src/jalview/appletgui/SplitFrame.java index ed531d3..777e307 100644 --- a/src/jalview/appletgui/SplitFrame.java +++ b/src/jalview/appletgui/SplitFrame.java @@ -187,9 +187,9 @@ public class SplitFrame extends EmbmenuFrame createSplitFrameWindow(embedded, applet); validate(); topFrame.alignPanel.adjustAnnotationHeight(); - topFrame.alignPanel.paintAlignment(true); + topFrame.alignPanel.paintAlignment(true, true); bottomFrame.alignPanel.adjustAnnotationHeight(); - bottomFrame.alignPanel.paintAlignment(true); + bottomFrame.alignPanel.paintAlignment(true, true); } /** diff --git a/src/jalview/appletgui/UserDefinedColours.java b/src/jalview/appletgui/UserDefinedColours.java index d1c0e1b..6831a73 100644 --- a/src/jalview/appletgui/UserDefinedColours.java +++ b/src/jalview/appletgui/UserDefinedColours.java @@ -64,7 +64,7 @@ public class UserDefinedColours extends Panel Button selectedButton; - Vector oldColours = new Vector(); + Vector oldColours = new Vector<>(); ColourSchemeI oldColourScheme; @@ -520,7 +520,7 @@ public class UserDefinedColours extends Panel ap.av.isIgnoreGapsConsensus()); } ap.seqPanel.seqCanvas.img = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); } else if (jmol != null) { @@ -599,7 +599,7 @@ public class UserDefinedColours extends Panel { ap.av.setGlobalColourScheme(oldColourScheme); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } frame.setVisible(false); diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java index dd05843..460c2b3 100644 --- a/src/jalview/controller/AlignViewController.java +++ b/src/jalview/controller/AlignViewController.java @@ -52,14 +52,6 @@ public class AlignViewController implements AlignViewControllerI */ private AlignViewControllerGuiI avcg; - @Override - protected void finalize() throws Throwable - { - viewport = null; - alignPanel = null; - avcg = null; - }; - public AlignViewController(AlignViewControllerGuiI alignFrame, AlignViewportI viewport, AlignmentViewPanel alignPanel) { @@ -190,7 +182,7 @@ public class AlignViewController implements AlignViewControllerI if (changed) { viewport.setColumnSelection(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1) - bs.cardinality() @@ -215,7 +207,7 @@ public class AlignViewController implements AlignViewControllerI if (!extendCurrent) { cs.clear(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } } return false; @@ -337,7 +329,7 @@ public class AlignViewController implements AlignViewControllerI AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method); avcg.addHistoryItem(new OrderCommand(methodText, oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } @@ -375,7 +367,7 @@ public class AlignViewController implements AlignViewControllerI { avcg.getFeatureSettingsUI().discoverAllFeatureData(); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } return featuresFile; @@ -414,7 +406,7 @@ public class AlignViewController implements AlignViewControllerI if (changed) { viewport.setColumnSelection(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1) - bs.cardinality() @@ -438,7 +430,7 @@ public class AlignViewController implements AlignViewControllerI if (!extendCurrent) { cs.clear(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } } return false; diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index c464af2..09facbf 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -241,19 +241,6 @@ public class AlignmentAnnotation private boolean isrna; - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - sequenceRef = null; - groupRef = null; - super.finalize(); - } - public static int getGraphValueFromString(String string) { if (string.equalsIgnoreCase("BAR_GRAPH")) diff --git a/src/jalview/datamodel/CigarArray.java b/src/jalview/datamodel/CigarArray.java index b6224c2..1723f1d 100644 --- a/src/jalview/datamodel/CigarArray.java +++ b/src/jalview/datamodel/CigarArray.java @@ -170,32 +170,30 @@ public class CigarArray extends CigarBase hideStart = region[0]; hideEnd = region[1]; // edit hidden regions to selection range - if (hideStart < last) + + // just move on if hideEnd is before last + if (hideEnd < last) { - if (hideEnd > last) - { - hideStart = last; - } - else - { - continue; - } + continue; } - + // exit if next region is after end if (hideStart > end) { break; } - if (hideEnd > end) + // truncate region at start if last falls in region + if ((hideStart < last) && (hideEnd >= last)) { - hideEnd = end; + hideStart = last; } - if (hideStart > hideEnd) + // truncate region at end if end falls in region + if (hideEnd > end) // already checked that hideStart<=end { - break; + hideEnd = end; } + /** * form operations... */ @@ -207,7 +205,7 @@ public class CigarArray extends CigarBase last = hideEnd + 1; } // Final match if necessary. - if (last < end) + if (last <= end) { addOperation(CigarArray.M, end - last + 1); } diff --git a/src/jalview/datamodel/Mapping.java b/src/jalview/datamodel/Mapping.java index 328b96a..b5184fb 100644 --- a/src/jalview/datamodel/Mapping.java +++ b/src/jalview/datamodel/Mapping.java @@ -693,19 +693,6 @@ public class Mapping to = tto; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - map = null; - to = null; - super.finalize(); - } - /** * Returns an iterator which can serve up the aligned codon column positions * and their corresponding peptide products diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 2f1da7f..d254a17 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -38,8 +38,6 @@ import java.util.List; import java.util.ListIterator; import java.util.Vector; -import com.stevesoft.pat.Regex; - import fr.orsay.lri.varna.models.rna.RNA; /** @@ -51,11 +49,6 @@ import fr.orsay.lri.varna.models.rna.RNA; */ public class Sequence extends ASequence implements SequenceI { - private static final Regex limitrx = new Regex( - "[/][0-9]{1,}[-][0-9]{1,}$"); - - private static final Regex endrx = new Regex("[0-9]{1,}$"); - SequenceI datasetSequence; String name; @@ -84,12 +77,7 @@ public class Sequence extends ASequence implements SequenceI */ Vector annotation; - /** - * The index of the sequence in a MSA - */ - int index = -1; - - private SequenceFeatures sequenceFeatureStore; + private SequenceFeaturesI sequenceFeatureStore; /* * A cursor holding the approximate current view position to the sequence, @@ -151,6 +139,10 @@ public class Sequence extends ASequence implements SequenceI checkValidRange(); } + /** + * If 'name' ends in /i-j, where i >= j > 0 are integers, extracts i and j as + * start and end respectively and removes the suffix from the name + */ void parseId() { if (name == null) @@ -159,17 +151,37 @@ public class Sequence extends ASequence implements SequenceI "POSSIBLE IMPLEMENTATION ERROR: null sequence name passed to constructor."); name = ""; } - // Does sequence have the /start-end signature? - if (limitrx.search(name)) + int slashPos = name.lastIndexOf('/'); + if (slashPos > -1 && slashPos < name.length() - 1) { - name = limitrx.left(); - endrx.search(limitrx.stringMatched()); - setStart(Integer.parseInt(limitrx.stringMatched().substring(1, - endrx.matchedFrom() - 1))); - setEnd(Integer.parseInt(endrx.stringMatched())); + String suffix = name.substring(slashPos + 1); + String[] range = suffix.split("-"); + if (range.length == 2) + { + try + { + int from = Integer.valueOf(range[0]); + int to = Integer.valueOf(range[1]); + if (from > 0 && to >= from) + { + name = name.substring(0, slashPos); + setStart(from); + setEnd(to); + checkValidRange(); + } + } catch (NumberFormatException e) + { + // leave name unchanged if suffix is invalid + } + } } } + /** + * Ensures that 'end' is not before the end of the sequence, that is, + * (end-start+1) is at least as long as the count of ungapped positions. Note + * that end is permitted to be beyond the end of the sequence data. + */ void checkValidRange() { // Note: JAL-774 : @@ -178,7 +190,7 @@ public class Sequence extends ASequence implements SequenceI int endRes = 0; for (int j = 0; j < sequence.length; j++) { - if (!jalview.util.Comparison.isGap(sequence[j])) + if (!Comparison.isGap(sequence[j])) { endRes++; } @@ -453,15 +465,15 @@ public class Sequence extends ASequence implements SequenceI } /** - * DOCUMENT ME! + * Sets the sequence name. If the name ends in /start-end, then the start-end + * values are parsed out and set, and the suffix is removed from the name. * - * @param name - * DOCUMENT ME! + * @param theName */ @Override - public void setName(String name) + public void setName(String theName) { - this.name = name; + this.name = theName; this.parseId(); } @@ -1665,30 +1677,6 @@ public class Sequence extends ASequence implements SequenceI } } - /** - * @return The index (zero-based) on this sequence in the MSA. It returns - * {@code -1} if this information is not available. - */ - @Override - public int getIndex() - { - return index; - } - - /** - * Defines the position of this sequence in the MSA. Use the value {@code -1} - * if this information is undefined. - * - * @param The - * position for this sequence. This value is zero-based (zero for - * this first sequence) - */ - @Override - public void setIndex(int value) - { - index = value; - } - @Override public void setRNA(RNA r) { @@ -1827,7 +1815,9 @@ public class Sequence extends ASequence implements SequenceI * and we may have included adjacent or enclosing features; * remove any that are not enclosing, non-contact features */ - if (endPos > this.end || Comparison.isGap(sequence[toColumn - 1])) + boolean endColumnIsGapped = toColumn > 0 && toColumn <= sequence.length + && Comparison.isGap(sequence[toColumn - 1]); + if (endPos > this.end || endColumnIsGapped) { ListIterator it = result.listIterator(); while (it.hasNext()) diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index 6e6d1aa..9ad0a61 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -449,17 +449,6 @@ public interface SequenceI extends ASequenceI public void transferAnnotation(SequenceI entry, Mapping mp); /** - * @param index - * The sequence index in the MSA - */ - public void setIndex(int index); - - /** - * @return The index of the sequence in the alignment - */ - public int getIndex(); - - /** * @return The RNA of the sequence in the alignment */ diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index ad01324..50dfa90 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -161,8 +161,9 @@ public class EnsemblGene extends EnsemblSeqProxy } /** - * Converts a query, which may contain one or more gene or transcript - * identifiers, into a non-redundant list of gene identifiers. + * Converts a query, which may contain one or more gene, transcript, or + * external (to Ensembl) identifiers, into a non-redundant list of gene + * identifiers. * * @param accessions * @return @@ -173,54 +174,30 @@ public class EnsemblGene extends EnsemblSeqProxy for (String acc : accessions.split(getAccessionSeparator())) { - if (isGeneIdentifier(acc)) - { - if (!geneIds.contains(acc)) - { - geneIds.add(acc); - } - } - /* - * if given a transcript id, look up its gene parent + * First try lookup as an Ensembl (gene or transcript) identifier */ - else if (isTranscriptIdentifier(acc)) + String geneId = new EnsemblLookup(getDomain()).getGeneId(acc); + if (geneId != null) { - String geneId = new EnsemblLookup(getDomain()).getParent(acc); - if (geneId != null && !geneIds.contains(geneId)) + if (!geneIds.contains(geneId)) { geneIds.add(geneId); } } - else if (isProteinIdentifier(acc)) - { - String tscriptId = new EnsemblLookup(getDomain()).getParent(acc); - if (tscriptId != null) - { - String geneId = new EnsemblLookup(getDomain()) - .getParent(tscriptId); - - if (geneId != null && !geneIds.contains(geneId)) - { - geneIds.add(geneId); - } - } - // NOTE - acc is lost if it resembles an ENS.+ ID but isn't actually - // resolving to one... e.g. ENSMICP00000009241 - } - /* - * if given a gene or other external name, lookup and fetch - * the corresponding gene for all model organisms - */ else { + /* + * if given a gene or other external name, lookup and fetch + * the corresponding gene for all model organisms + */ List ids = new EnsemblSymbol(getDomain(), getDbSource(), - getDbVersion()).getIds(acc); - for (String geneId : ids) + getDbVersion()).getGeneIds(acc); + for (String id : ids) { - if (!geneIds.contains(geneId)) + if (!geneIds.contains(id)) { - geneIds.add(geneId); + geneIds.add(id); } } } @@ -229,30 +206,6 @@ public class EnsemblGene extends EnsemblSeqProxy } /** - * Attempts to get Ensembl stable identifiers for model organisms for a gene - * name by calling the xrefs symbol REST service to resolve the gene name. - * - * @param query - * @return - */ - protected String getGeneIdentifiersForName(String query) - { - List ids = new EnsemblSymbol(getDomain(), getDbSource(), - getDbVersion()).getIds(query); - if (ids != null) - { - for (String id : ids) - { - if (isGeneIdentifier(id)) - { - return id; - } - } - } - return null; - } - - /** * Constructs all transcripts for the gene, as identified by "transcript" * features whose Parent is the requested gene. The coding transcript * sequences (i.e. with introns omitted) are added to the alignment. diff --git a/src/jalview/ext/ensembl/EnsemblGenomes.java b/src/jalview/ext/ensembl/EnsemblGenomes.java index ef46a5b..bbd1f26 100644 --- a/src/jalview/ext/ensembl/EnsemblGenomes.java +++ b/src/jalview/ext/ensembl/EnsemblGenomes.java @@ -39,12 +39,6 @@ public class EnsemblGenomes extends EnsemblGene } @Override - public boolean isGeneIdentifier(String query) - { - return true; - } - - @Override public String getDbName() { return "EnsemblGenomes"; @@ -53,7 +47,10 @@ public class EnsemblGenomes extends EnsemblGene @Override public String getTestQuery() { - return "DDB_G0283883"; + /* + * Salmonella gene, Uniprot Q8Z9G6, EMBLCDS CAD01290 + */ + return "CAD01290"; } @Override diff --git a/src/jalview/ext/ensembl/EnsemblInfo.java b/src/jalview/ext/ensembl/EnsemblInfo.java index 3108194..7668941 100644 --- a/src/jalview/ext/ensembl/EnsemblInfo.java +++ b/src/jalview/ext/ensembl/EnsemblInfo.java @@ -70,11 +70,6 @@ class EnsemblInfo // flag set to true if REST major version is not the one expected boolean restMajorVersionMismatch; - /* - * absolute time to wait till if we overloaded the REST service - */ - long retryAfter; - /** * Constructor given expected REST version number e.g 4.5 or 3.4.3 * diff --git a/src/jalview/ext/ensembl/EnsemblLookup.java b/src/jalview/ext/ensembl/EnsemblLookup.java index eb8f90e..31da9c0 100644 --- a/src/jalview/ext/ensembl/EnsemblLookup.java +++ b/src/jalview/ext/ensembl/EnsemblLookup.java @@ -43,6 +43,13 @@ import org.json.simple.parser.ParseException; public class EnsemblLookup extends EnsemblRestClient { + private static final String OBJECT_TYPE_TRANSLATION = "Translation"; + private static final String PARENT = "Parent"; + private static final String OBJECT_TYPE_TRANSCRIPT = "Transcript"; + private static final String ID = "id"; + private static final String OBJECT_TYPE_GENE = "Gene"; + private static final String OBJECT_TYPE = "object_type"; + /** * Default constructor (to use rest.ensembl.org) */ @@ -87,7 +94,7 @@ public class EnsemblLookup extends EnsemblRestClient protected URL getUrl(String identifier) { String url = getDomain() + "/lookup/id/" + identifier - + "?content-type=application/json"; + + CONTENT_TYPE_JSON; try { return new URL(url); @@ -122,7 +129,7 @@ public class EnsemblLookup extends EnsemblRestClient * @param identifier * @return */ - public String getParent(String identifier) + public String getGeneId(String identifier) { List ids = Arrays.asList(new String[] { identifier }); @@ -134,7 +141,7 @@ public class EnsemblLookup extends EnsemblRestClient { br = getHttpResponse(url, ids); } - return (parseResponse(br)); + return br == null ? null : parseResponse(br); } catch (IOException e) { // ignore @@ -155,8 +162,10 @@ public class EnsemblLookup extends EnsemblRestClient } /** - * Parses "Parent" from the JSON response and returns the value, or null if - * not found + * Parses the JSON response and returns the gene identifier, or null if not + * found. If the returned object_type is Gene, returns the id, if Transcript + * returns the Parent. If it is Translation (peptide identifier), then the + * Parent is the transcript identifier, so we redo the search with this value. * * @param br * @return @@ -164,17 +173,42 @@ public class EnsemblLookup extends EnsemblRestClient */ protected String parseResponse(BufferedReader br) throws IOException { - String parent = null; + String geneId = null; JSONParser jp = new JSONParser(); try { JSONObject val = (JSONObject) jp.parse(br); - parent = val.get("Parent").toString(); + String type = val.get(OBJECT_TYPE).toString(); + if (OBJECT_TYPE_GENE.equalsIgnoreCase(type)) + { + geneId = val.get(ID).toString(); + } + else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type)) + { + geneId = val.get(PARENT).toString(); + } + else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type)) + { + String transcriptId = val.get(PARENT).toString(); + try + { + geneId = getGeneId(transcriptId); + } catch (StackOverflowError e) + { + /* + * unlikely data condition error! + */ + System.err + .println("** Ensembl lookup " + + getUrl(transcriptId).toString() + + " looping on Parent!"); + } + } } catch (ParseException e) { // ignore } - return parent; + return geneId; } } diff --git a/src/jalview/ext/ensembl/EnsemblProtein.java b/src/jalview/ext/ensembl/EnsemblProtein.java index 1554a0b..99006aa 100644 --- a/src/jalview/ext/ensembl/EnsemblProtein.java +++ b/src/jalview/ext/ensembl/EnsemblProtein.java @@ -23,8 +23,6 @@ package jalview.ext.ensembl; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceFeature; -import java.util.List; - import com.stevesoft.pat.Regex; /** diff --git a/src/jalview/ext/ensembl/EnsemblRestClient.java b/src/jalview/ext/ensembl/EnsemblRestClient.java index ad6c70c..b1bc8e5 100644 --- a/src/jalview/ext/ensembl/EnsemblRestClient.java +++ b/src/jalview/ext/ensembl/EnsemblRestClient.java @@ -31,6 +31,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; +import java.net.ProtocolException; import java.net.URL; import java.util.HashMap; import java.util.List; @@ -42,8 +43,6 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; -import com.stevesoft.pat.Regex; - /** * Base class for Ensembl REST service clients * @@ -55,15 +54,21 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher private static final int CONNECT_TIMEOUT_MS = 10 * 1000; // 10 seconds + private static final int MAX_RETRIES = 3; + + private static final int HTTP_OK = 200; + + private static final int HTTP_OVERLOAD = 429; + /* * update these constants when Jalview has been checked / updated for * changes to Ensembl REST API (ref JAL-2105) * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log * @see http://rest.ensembl.org/info/rest?content-type=application/json */ - private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "5.0"; + private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "6.0"; - private static final String LATEST_ENSEMBL_REST_VERSION = "5.0"; + private static final String LATEST_ENSEMBL_REST_VERSION = "6.1"; private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log"; @@ -76,18 +81,11 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr - private static final Regex PROTEIN_REGEX = new Regex( - "(ENS)([A-Z]{3}|)P[0-9]{11}$"); - - private static final Regex TRANSCRIPT_REGEX = new Regex( - "(ENS)([A-Z]{3}|)T[0-9]{11}$"); - - private static final Regex GENE_REGEX = new Regex( - "(ENS)([A-Z]{3}|)G[0-9]{11}$"); + protected static final String CONTENT_TYPE_JSON = "?content-type=application/json"; static { - domainData = new HashMap(); + domainData = new HashMap<>(); domainData.put(ENSEMBL_REST, new EnsemblInfo(ENSEMBL_REST, LATEST_ENSEMBL_REST_VERSION)); domainData.put(ENSEMBL_GENOMES_REST, new EnsemblInfo( @@ -114,42 +112,6 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher setDomain(d); } - /** - * Answers true if the query matches the regular expression pattern for an - * Ensembl transcript stable identifier - * - * @param query - * @return - */ - public boolean isTranscriptIdentifier(String query) - { - return query == null ? false : TRANSCRIPT_REGEX.search(query); - } - - /** - * Answers true if the query matches the regular expression pattern for an - * Ensembl protein stable identifier - * - * @param query - * @return - */ - public boolean isProteinIdentifier(String query) - { - return query == null ? false : PROTEIN_REGEX.search(query); - } - - /** - * Answers true if the query matches the regular expression pattern for an - * Ensembl gene stable identifier - * - * @param query - * @return - */ - public boolean isGeneIdentifier(String query) - { - return query == null ? false : GENE_REGEX.search(query); - } - @Override public boolean queryInProgress() { @@ -204,21 +166,25 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher * @see http://rest.ensembl.org/documentation/info/ping * @return */ - private boolean checkEnsembl() + boolean checkEnsembl() { BufferedReader br = null; try { // note this format works for both ensembl and ensemblgenomes // info/ping.json works for ensembl only (March 2016) - URL ping = new URL( - getDomain() + "/info/ping?content-type=application/json"); + URL ping = new URL(getDomain() + "/info/ping" + CONTENT_TYPE_JSON); /* * expect {"ping":1} if ok * if ping takes more than 2 seconds to respond, treat as if unavailable */ br = getHttpResponse(ping, null, 2 * 1000); + if (br == null) + { + // error reponse status + return false; + } JSONParser jp = new JSONParser(); JSONObject val = (JSONObject) jp.parse(br); String pingString = val.get("ping").toString(); @@ -281,7 +247,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher } /** - * Writes the HTTP request and gets the response as a reader. + * Sends the HTTP request and gets the response as a reader * * @param url * @param ids @@ -295,7 +261,56 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher protected BufferedReader getHttpResponse(URL url, List ids, int readTimeout) throws IOException { - // long now = System.currentTimeMillis(); + int retriesLeft = MAX_RETRIES; + HttpURLConnection connection = null; + int responseCode = 0; + + while (retriesLeft > 0) + { + connection = tryConnection(url, ids, readTimeout); + responseCode = connection.getResponseCode(); + if (responseCode == HTTP_OVERLOAD) // 429 + { + retriesLeft--; + checkRetryAfter(connection); + } + else + { + retriesLeft = 0; + } + } + if (responseCode != HTTP_OK) // 200 + { + /* + * note: a GET request for an invalid id returns an error code e.g. 415 + * but POST request returns 200 and an empty Fasta response + */ + System.err.println("Response code " + responseCode + " for " + url); + return null; + } + + InputStream response = connection.getInputStream(); + + // System.out.println(getClass().getName() + " took " + // + (System.currentTimeMillis() - now) + "ms to fetch"); + + BufferedReader reader = null; + reader = new BufferedReader(new InputStreamReader(response, "UTF-8")); + return reader; + } + + /** + * @param url + * @param ids + * @param readTimeout + * @return + * @throws IOException + * @throws ProtocolException + */ + protected HttpURLConnection tryConnection(URL url, List ids, + int readTimeout) throws IOException, ProtocolException + { + // System.out.println(System.currentTimeMillis() + " " + url); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); /* @@ -320,77 +335,40 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher { writePostBody(connection, ids); } - - int responseCode = connection.getResponseCode(); - - if (responseCode != 200) - { - /* - * note: a GET request for an invalid id returns an error code e.g. 415 - * but POST request returns 200 and an empty Fasta response - */ - System.err.println("Response code " + responseCode + " for " + url); - return null; - } - // get content - InputStream response = connection.getInputStream(); - - // System.out.println(getClass().getName() + " took " - // + (System.currentTimeMillis() - now) + "ms to fetch"); - - checkRateLimits(connection); - - BufferedReader reader = null; - reader = new BufferedReader(new InputStreamReader(response, "UTF-8")); - return reader; + return connection; } /** - * Inspect response headers for any sign of server overload and respect any - * 'retry-after' directive + * Inspects response headers for a 'retry-after' directive, and waits for the + * directed period (if less than 10 seconds) * * @see https://github.com/Ensembl/ensembl-rest/wiki/Rate-Limits * @param connection */ - void checkRateLimits(HttpURLConnection connection) + void checkRetryAfter(HttpURLConnection connection) { - // number of requests allowed per time interval: - String limit = connection.getHeaderField("X-RateLimit-Limit"); - // length of quota time interval in seconds: - // String period = connection.getHeaderField("X-RateLimit-Period"); - // seconds remaining until usage quota is reset: - String reset = connection.getHeaderField("X-RateLimit-Reset"); - // number of requests remaining from quota for current period: - String remaining = connection.getHeaderField("X-RateLimit-Remaining"); - // number of seconds to wait before retrying (if remaining == 0) String retryDelay = connection.getHeaderField("Retry-After"); // to test: // retryDelay = "5"; - EnsemblInfo info = domainData.get(getDomain()); if (retryDelay != null) { - System.err.println("Ensembl REST service rate limit exceeded, wait " - + retryDelay + " seconds before retrying"); try { - info.retryAfter = System.currentTimeMillis() - + (1000 * Integer.valueOf(retryDelay)); - } catch (NumberFormatException e) + int retrySecs = Integer.valueOf(retryDelay); + if (retrySecs > 0 && retrySecs < 10) + { + System.err + .println("Ensembl REST service rate limit exceeded, waiting " + + retryDelay + " seconds before retrying"); + Thread.sleep(1000 * retrySecs); + } + } catch (NumberFormatException | InterruptedException e) { - System.err - .println("Unexpected value for Retry-After: " + retryDelay); + System.err.println("Error handling Retry-After: " + e.getMessage()); } } - else - { - info.retryAfter = 0; - // debug: - // System.out.println(String.format( - // "%s Ensembl requests remaining of %s (reset in %ss)", - // remaining, limit, reset)); - } } /** @@ -408,20 +386,6 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher long now = System.currentTimeMillis(); /* - * check if we are waiting for 'Retry-After' to expire - */ - if (info.retryAfter > now) - { - System.err.println("Still " + (1 + (info.retryAfter - now) / 1000) - + " secs to wait before retrying Ensembl"); - return false; - } - else - { - info.retryAfter = 0; - } - - /* * recheck if Ensembl is up if it was down, or the recheck period has elapsed */ boolean retestAvailability = (now @@ -497,9 +461,12 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher URL url = null; try { - url = new URL( - getDomain() + "/info/rest?content-type=application/json"); + url = new URL(getDomain() + "/info/rest" + CONTENT_TYPE_JSON); BufferedReader br = getHttpResponse(url, null); + if (br == null) + { + return; + } JSONObject val = (JSONObject) jp.parse(br); String version = val.get("release").toString(); String majorVersion = version.substring(0, version.indexOf(".")); @@ -558,18 +525,35 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher { JSONParser jp = new JSONParser(); URL url = null; + BufferedReader br = null; + try { - url = new URL( - getDomain() + "/info/data?content-type=application/json"); - BufferedReader br = getHttpResponse(url, null); - JSONObject val = (JSONObject) jp.parse(br); - JSONArray versions = (JSONArray) val.get("releases"); - domainData.get(getDomain()).dataVersion = versions.get(0).toString(); + url = new URL(getDomain() + "/info/data" + CONTENT_TYPE_JSON); + br = getHttpResponse(url, null); + if (br != null) + { + JSONObject val = (JSONObject) jp.parse(br); + JSONArray versions = (JSONArray) val.get("releases"); + domainData.get(getDomain()).dataVersion = versions.get(0) + .toString(); + } } catch (Throwable t) { System.err.println( "Error checking Ensembl data version: " + t.getMessage()); + } finally + { + if (br != null) + { + try + { + br.close(); + } catch (IOException e) + { + // ignore + } + } } } diff --git a/src/jalview/ext/ensembl/EnsemblSymbol.java b/src/jalview/ext/ensembl/EnsemblSymbol.java index 9f86731..75598a0 100644 --- a/src/jalview/ext/ensembl/EnsemblSymbol.java +++ b/src/jalview/ext/ensembl/EnsemblSymbol.java @@ -42,6 +42,10 @@ import org.json.simple.parser.ParseException; */ public class EnsemblSymbol extends EnsemblXref { + private static final String GENE = "gene"; + private static final String TYPE = "type"; + private static final String ID = "id"; + /** * Constructor given the target domain to fetch data from * @@ -73,8 +77,9 @@ public class EnsemblSymbol extends EnsemblXref while (rvals.hasNext()) { JSONObject val = (JSONObject) rvals.next(); - String id = val.get("id").toString(); - if (id != null && isGeneIdentifier(id)) + String id = val.get(ID).toString(); + String type = val.get(TYPE).toString(); + if (id != null && GENE.equals(type)) { result = id; break; @@ -87,12 +92,31 @@ public class EnsemblSymbol extends EnsemblXref return result; } - protected URL getUrl(String id, Species species) + /** + * Constructs the URL for the REST symbol endpoint + * + * @param id + * the accession id (Ensembl or external) + * @param species + * a species name recognisable by Ensembl + * @param type + * an optional type to filter the response (gene, transcript, + * translation) + * @return + */ + protected URL getUrl(String id, Species species, String... type) { - String url = getDomain() + "/xrefs/symbol/" + species.toString() + "/" - + id + "?content-type=application/json"; + StringBuilder sb = new StringBuilder(); + sb.append(getDomain()).append("/xrefs/symbol/") + .append(species.toString()).append("/").append(id) + .append(CONTENT_TYPE_JSON); + for (String t : type) + { + sb.append("&object_type=").append(t); + } try { + String url = sb.toString(); return new URL(url); } catch (MalformedURLException e) { @@ -107,7 +131,7 @@ public class EnsemblSymbol extends EnsemblXref * @param identifier * @return */ - public List getIds(String identifier) + public List getGeneIds(String identifier) { List result = new ArrayList(); List ids = new ArrayList(); @@ -119,19 +143,20 @@ public class EnsemblSymbol extends EnsemblXref { for (String query : queries) { - for (Species taxon : Species.values()) + for (Species taxon : Species.getModelOrganisms()) { - if (taxon.isModelOrganism()) + URL url = getUrl(query, taxon, GENE); + if (url != null) { - URL url = getUrl(query, taxon); - if (url != null) - { - br = getHttpResponse(url, ids); - } - String geneId = parseSymbolResponse(br); - if (geneId != null) + br = getHttpResponse(url, ids); + if (br != null) { - result.add(geneId); + String geneId = parseSymbolResponse(br); + System.out.println(url + " returned " + geneId); + if (geneId != null && !result.contains(geneId)) + { + result.add(geneId); + } } } } diff --git a/src/jalview/ext/ensembl/EnsemblXref.java b/src/jalview/ext/ensembl/EnsemblXref.java index c0b00b1..27c448e 100644 --- a/src/jalview/ext/ensembl/EnsemblXref.java +++ b/src/jalview/ext/ensembl/EnsemblXref.java @@ -124,8 +124,11 @@ class EnsemblXref extends EnsemblRestClient if (url != null) { br = getHttpResponse(url, ids); + if (br != null) + { + result = parseResponse(br); + } } - return (parseResponse(br)); } catch (IOException e) { // ignore @@ -168,16 +171,13 @@ class EnsemblXref extends EnsemblRestClient while (rvals.hasNext()) { JSONObject val = (JSONObject) rvals.next(); - String dbName = val.get("dbname").toString(); - if (dbName.equals(GO_GENE_ONTOLOGY)) - { - continue; - } + String db = val.get("dbname").toString(); String id = val.get("primary_id").toString(); - if (dbName != null && id != null) + if (db != null && id != null + && !GO_GENE_ONTOLOGY.equals(db)) { - dbName = DBRefUtils.getCanonicalName(dbName); - DBRefEntry dbref = new DBRefEntry(dbName, getXRefVersion(), id); + db = DBRefUtils.getCanonicalName(db); + DBRefEntry dbref = new DBRefEntry(db, getXRefVersion(), id); result.add(dbref); } } @@ -211,7 +211,7 @@ class EnsemblXref extends EnsemblRestClient protected URL getUrl(String identifier) { String url = getDomain() + "/xrefs/id/" + identifier - + "?content-type=application/json&all_levels=1"; + + CONTENT_TYPE_JSON + "&all_levels=1"; try { return new URL(url); diff --git a/src/jalview/ext/ensembl/Species.java b/src/jalview/ext/ensembl/Species.java index af01225..cc5465e 100644 --- a/src/jalview/ext/ensembl/Species.java +++ b/src/jalview/ext/ensembl/Species.java @@ -20,6 +20,9 @@ */ package jalview.ext.ensembl; +import java.util.HashSet; +import java.util.Set; + /** * Selected species identifiers used by Ensembl * @@ -38,6 +41,18 @@ enum Species chimpanzee(false), cat(false), zebrafish(true), chicken(true), dmelanogaster(true); + static Set modelOrganisms = new HashSet<>(); + + static + { + for (Species s : values()) + { + if (s.isModelOrganism()) + { + modelOrganisms.add(s); + } + } + } boolean modelOrganism; private Species(boolean model) @@ -49,4 +64,9 @@ enum Species { return modelOrganism; } + + public static Set getModelOrganisms() + { + return modelOrganisms; + } } diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 96dfcfe..41bc116 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -27,6 +27,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import jalview.io.StructureFile; import jalview.schemes.ColourSchemeI; @@ -72,7 +73,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel */ private boolean associateNewStructs = false; - Vector atomsPicked = new Vector(); + Vector atomsPicked = new Vector<>(); private List chainNames; @@ -610,7 +611,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel } if (modelFileNames == null) { - List mset = new ArrayList(); + List mset = new ArrayList<>(); _modelFileNameMap = new int[viewer.ms.mc]; String m = viewer.ms.getModelFileName(0); if (m != null) @@ -670,7 +671,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel @Override public synchronized String[] getStructureFiles() { - List mset = new ArrayList(); + List mset = new ArrayList<>(); if (viewer == null) { return new String[0]; @@ -975,6 +976,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1], (String) data[0]); // also highlight in alignment + // deliberate fall through case HOVER: notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1], (String) data[0]); @@ -1059,8 +1061,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel fileLoadingError = null; String[] oldmodels = modelFileNames; modelFileNames = null; - chainNames = new ArrayList(); - chainFile = new Hashtable(); + chainNames = new ArrayList<>(); + chainFile = new Hashtable<>(); boolean notifyLoaded = false; String[] modelfilenames = getStructureFiles(); // first check if we've lost any structures @@ -1126,7 +1128,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel // see JAL-623 - need method of matching pasted data up { pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe], - pdbfile, DataSourceType.PASTE); + pdbfile, DataSourceType.PASTE, + getIProgressIndicator()); getPdbEntry(modelnum).setFile("INLINE" + pdb.getId()); matches = true; foundEntry = true; @@ -1158,7 +1161,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel } // Explicitly map to the filename used by Jmol ; pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe], - fileName, protocol); + fileName, protocol, getIProgressIndicator()); // pdbentry[pe].getFile(), protocol); } @@ -1226,6 +1229,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel return chainNames; } + protected abstract IProgressIndicator getIProgressIndicator(); + public void notifyNewPickingModeMeasurement(int iatom, String strMeasure) { notifyAtomPicked(iatom, strMeasure, null); diff --git a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java index f923f7f..39d6704 100644 --- a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java +++ b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java @@ -120,7 +120,7 @@ public class AtomSpecModel for (String chain : modelData.keySet()) { - chain = chain.trim(); + chain = " ".equals(chain) ? chain : chain.trim(); List rangeList = modelData.get(chain); @@ -192,9 +192,10 @@ public class AtomSpecModel { sb.append(start).append("-").append(end); } - if (chain.length() > 0) - { - sb.append(".").append(chain); + + sb.append("."); + if (!" ".equals(chain)) { + sb.append(chain); } } } diff --git a/src/jalview/fts/api/GFTSPanelI.java b/src/jalview/fts/api/GFTSPanelI.java index 99c0c51..974cc88 100644 --- a/src/jalview/fts/api/GFTSPanelI.java +++ b/src/jalview/fts/api/GFTSPanelI.java @@ -145,4 +145,11 @@ public interface GFTSPanelI * @return */ public String getCacheKey(); + + /** + * + * @return user preference name for configuring this FTS search's autosearch + * checkbox + */ + public String getAutosearchPreference(); } diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java index c0d005f..9802d4b 100644 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@ -56,6 +56,7 @@ import java.util.List; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JInternalFrame; @@ -88,6 +89,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI protected JInternalFrame mainFrame = new JInternalFrame( getFTSFrameTitle()); + protected JTabbedPane tabs = new JTabbedPane(); protected IProgressIndicator progressIndicator; protected JComboBox cmb_searchTarget = new JComboBox(); @@ -98,6 +100,8 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI protected JButton btn_cancel = new JButton(); + protected JCheckBox btn_autosearch = new JCheckBox(); + protected JvCacheableInputBox txt_search; protected SequenceFetcher seqFetcher; @@ -239,16 +243,38 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI public GFTSPanel() { + this(null); + } + + public GFTSPanel(SequenceFetcher fetcher) + { try { + if (fetcher == null) + { + tabs = null; + } jbInit(); + if (fetcher != null) + { + tabs.addTab(MessageManager.getString("label.retrieve_ids"), + fetcher); + fetcher.setDatabaseChooserVisible(false); + fetcher.embedWithFTSPanel(this); + } mainFrame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); + final JPanel ftsPanel = this; mainFrame.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { - txt_search.requestFocusInWindow(); + // TODO: make selected tab gain focus in correct widget + if (tabs != null + && tabs.getSelectedComponent() == ftsPanel) + { + txt_search.requestFocusInWindow(); + } } }); mainFrame.invalidate(); @@ -331,6 +357,20 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } }); + btn_autosearch.setText(MessageManager.getString("option.autosearch")); + btn_autosearch.setToolTipText( + MessageManager.getString("option.enable_disable_autosearch")); + btn_autosearch.setSelected( + jalview.bin.Cache.getDefault(getAutosearchPreference(), true)); + btn_autosearch.addActionListener(new java.awt.event.ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + jalview.bin.Cache.setProperty(getAutosearchPreference(), + Boolean.toString(btn_autosearch.isSelected())); + } + }); btn_back.setFont(new java.awt.Font("Verdana", 0, 12)); btn_back.setText(MessageManager.getString("action.back")); btn_back.addActionListener(new java.awt.event.ActionListener() @@ -511,8 +551,14 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI if (primaryKeyName.equalsIgnoreCase(getCmbSearchTarget() .getSelectedItem().toString())) { + // TODO: nicer to show the list in the result set before + // viewing in Jalview perhaps ? transferToSequenceFetcher(getTypedText()); } + else + { + performSearchAction(); + } } } }); @@ -522,12 +568,10 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI @Override public void actionPerformed(ActionEvent e) { - String typed = getTypedText(); - if (!typed.equalsIgnoreCase(lastSearchTerm)) + if (btn_autosearch.isSelected() + || txt_search.wasEnterPressed()) { - searchAction(true); - paginatorCart.clear(); - lastSearchTerm = typed; + performSearchAction(); } } }, false); @@ -549,6 +593,15 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } }); + txt_search.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + performSearchAction(); + } + }); final String searchTabTitle = MessageManager .getString("label.search_result"); final String configureCols = MessageManager @@ -613,6 +666,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI pnl_results.add(tabbedPane); pnl_inputs.add(cmb_searchTarget); pnl_inputs.add(txt_search); + pnl_inputs.add(btn_autosearch); pnl_inputs.add(lbl_loading); pnl_inputs.add(lbl_warning); pnl_inputs.add(lbl_blank); @@ -624,7 +678,17 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI this.add(pnl_results, java.awt.BorderLayout.CENTER); this.add(pnl_actions, java.awt.BorderLayout.SOUTH); mainFrame.setVisible(true); - mainFrame.setContentPane(this); + if (tabs != null) + { + tabs.setOpaque(true); + tabs.insertTab("Free Text Search", null, this, "", 0); + mainFrame.setContentPane(tabs); + tabs.setVisible(true); + } + else + { + mainFrame.setContentPane(this); + } mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); mainFrame.addInternalFrameListener( new javax.swing.event.InternalFrameAdapter() @@ -635,8 +699,6 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI closeAction(); } }); - mainFrame.setVisible(true); - mainFrame.setContentPane(this); mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); Integer x = getTempUserPrefs().get("FTSPanel.x"); Integer y = getTempUserPrefs().get("FTSPanel.y"); @@ -698,6 +760,18 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } + void performSearchAction() + { + String typed = getTypedText(); + if (typed != null && typed.length() > 0 + && !typed.equalsIgnoreCase(lastSearchTerm)) + { + searchAction(true); + paginatorCart.clear(); + lastSearchTerm = typed; + } + } + public boolean wantedFieldsUpdated() { if (previousWantedFields == null) @@ -774,7 +848,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } } - protected void btn_back_ActionPerformed() + public void btn_back_ActionPerformed() { closeAction(); new SequenceFetcher(progressIndicator); @@ -787,7 +861,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI btn_cancel.setEnabled(false); } - protected void btn_cancel_ActionPerformed() + public void btn_cancel_ActionPerformed() { closeAction(); } diff --git a/src/jalview/fts/service/pdb/PDBFTSPanel.java b/src/jalview/fts/service/pdb/PDBFTSPanel.java index 2a53ab9..053d91b 100644 --- a/src/jalview/fts/service/pdb/PDBFTSPanel.java +++ b/src/jalview/fts/service/pdb/PDBFTSPanel.java @@ -43,13 +43,15 @@ public class PDBFTSPanel extends GFTSPanel private static final String PDB_FTS_CACHE_KEY = "CACHE.PDB_FTS"; - public PDBFTSPanel(SequenceFetcher seqFetcher) + private static final String PDB_AUTOSEARCH = "FTS.PDB.AUTOSEARCH"; + + public PDBFTSPanel(SequenceFetcher fetcher) { - super(); + super(fetcher); pageLimit = PDBFTSRestClient.getInstance().getDefaultResponsePageSize(); - this.seqFetcher = seqFetcher; - this.progressIndicator = (seqFetcher == null) ? null - : seqFetcher.getProgressIndicator(); + this.seqFetcher = fetcher; + this.progressIndicator = (fetcher == null) ? null + : fetcher.getProgressIndicator(); } @Override @@ -86,15 +88,16 @@ public class PDBFTSPanel extends GFTSPanel request.setSearchTerm(searchTerm + ")"); request.setOffSet(offSet); request.setWantedFields(wantedFields); - FTSRestClientI pdbRestCleint = PDBFTSRestClient.getInstance(); + FTSRestClientI pdbRestClient = PDBFTSRestClient.getInstance(); FTSRestResponse resultList; try { - resultList = pdbRestCleint.executeRequest(request); + resultList = pdbRestClient.executeRequest(request); } catch (Exception e) { setErrorMessage(e.getMessage()); checkForErrors(); + setSearchInProgress(false); return; } @@ -280,4 +283,9 @@ public class PDBFTSPanel extends GFTSPanel return PDB_FTS_CACHE_KEY; } -} + @Override + public String getAutosearchPreference() + { + return PDB_AUTOSEARCH; + } +} \ No newline at end of file diff --git a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java index 2dad2f7..df54dea 100644 --- a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java +++ b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java @@ -40,18 +40,20 @@ public class UniprotFTSPanel extends GFTSPanel private static String defaultFTSFrameTitle = MessageManager .getString("label.uniprot_sequence_fetcher"); - private static Map tempUserPrefs = new HashMap(); + private static Map tempUserPrefs = new HashMap<>(); private static final String UNIPROT_FTS_CACHE_KEY = "CACHE.UNIPROT_FTS"; - public UniprotFTSPanel(SequenceFetcher seqFetcher) + private static final String UNIPROT_AUTOSEARCH = "FTS.UNIPROT.AUTOSEARCH"; + + public UniprotFTSPanel(SequenceFetcher fetcher) { - super(); + super(fetcher); pageLimit = UniProtFTSRestClient.getInstance() .getDefaultResponsePageSize(); - this.seqFetcher = seqFetcher; - this.progressIndicator = (seqFetcher == null) ? null - : seqFetcher.getProgressIndicator(); + this.seqFetcher = fetcher; + this.progressIndicator = (fetcher == null) ? null + : fetcher.getProgressIndicator(); } @Override @@ -85,17 +87,17 @@ public class UniprotFTSPanel extends GFTSPanel request.setSearchTerm(searchTerm); request.setOffSet(offSet); request.setWantedFields(wantedFields); - FTSRestClientI uniProtRestCleint = UniProtFTSRestClient + FTSRestClientI uniProtRestClient = UniProtFTSRestClient .getInstance(); FTSRestResponse resultList; try { - resultList = uniProtRestCleint.executeRequest(request); + resultList = uniProtRestClient.executeRequest(request); } catch (Exception e) { - e.printStackTrace(); setErrorMessage(e.getMessage()); checkForErrors(); + setSearchInProgress(false); return; } @@ -183,7 +185,7 @@ public class UniprotFTSPanel extends GFTSPanel { disableActionButtons(); StringBuilder selectedIds = new StringBuilder(); - HashSet selectedIdsSet = new HashSet(); + HashSet selectedIdsSet = new HashSet<>(); int primaryKeyColIndex = 0; try { @@ -237,4 +239,10 @@ public class UniprotFTSPanel extends GFTSPanel { return UNIPROT_FTS_CACHE_KEY; } + + @Override + public String getAutosearchPreference() + { + return UNIPROT_AUTOSEARCH; + } } diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 13b715e..298688b 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -163,8 +163,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignViewport viewport; - ViewportRanges vpRanges; - public AlignViewControllerI avc; List alignPanels = new ArrayList<>(); @@ -336,7 +334,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, progressBar = new ProgressBar(this.statusPanel, this.statusBar); } - vpRanges = viewport.getRanges(); avc = new jalview.controller.AlignViewController(this, viewport, alignPanel); if (viewport.getAlignmentConservationAnnotation() == null) @@ -654,9 +651,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { (viewport.cursorMode ? "on" : "off") })); if (viewport.cursorMode) { - alignPanel.getSeqPanel().seqCanvas.cursorX = vpRanges + ViewportRanges ranges = viewport.getRanges(); + alignPanel.getSeqPanel().seqCanvas.cursorX = ranges .getStartRes(); - alignPanel.getSeqPanel().seqCanvas.cursorY = vpRanges + alignPanel.getSeqPanel().seqCanvas.cursorY = ranges .getStartSeq(); } alignPanel.getSeqPanel().seqCanvas.repaint(); @@ -689,10 +687,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, break; } case KeyEvent.VK_PAGE_UP: - vpRanges.pageUp(); + viewport.getRanges().pageUp(); break; case KeyEvent.VK_PAGE_DOWN: - vpRanges.pageDown(); + viewport.getRanges().pageDown(); break; } } @@ -1711,7 +1709,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } viewport.getAlignment().moveSelectedSequencesByOne(sg, viewport.getHiddenRepSequences(), up); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } synchronized void slideSequences(boolean right, int size) @@ -2147,7 +2145,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { // propagate alignment changed. - vpRanges.setEndSeq(alignment.getHeight()); + viewport.getRanges().setEndSeq(alignment.getHeight()); if (annotationAdded) { // Duplicate sequence annotation in all views. @@ -2397,7 +2395,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -2423,7 +2421,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); } @@ -2448,7 +2446,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2479,7 +2477,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2488,7 +2486,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void invertColSel_actionPerformed(ActionEvent e) { viewport.invertColumnSelection(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); viewport.sendSelection(); } @@ -2548,7 +2546,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { trimRegion = new TrimRegionCommand("Remove Left", true, seqs, column, viewport.getAlignment()); - vpRanges.setStartRes(0); + viewport.getRanges().setStartRes(0); } else { @@ -2613,13 +2611,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // This is to maintain viewport position on first residue // of first sequence SequenceI seq = viewport.getAlignment().getSequenceAt(0); - int startRes = seq.findPosition(vpRanges.getStartRes()); + ViewportRanges ranges = viewport.getRanges(); + int startRes = seq.findPosition(ranges.getStartRes()); // ShiftList shifts; // viewport.getAlignment().removeGaps(shifts=new ShiftList()); // edit.alColumnChanges=shifts.getInverse(); // if (viewport.hasHiddenColumns) // viewport.getColumnSelection().compensateForEdits(shifts); - vpRanges.setStartRes(seq.findIndex(startRes) - 1); + ranges.setStartRes(seq.findIndex(startRes) - 1); viewport.firePropertyChange("alignment", null, viewport.getAlignment().getSequences()); @@ -2652,12 +2651,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // This is to maintain viewport position on first residue // of first sequence SequenceI seq = viewport.getAlignment().getSequenceAt(0); - int startRes = seq.findPosition(vpRanges.getStartRes()); + int startRes = seq.findPosition(viewport.getRanges().getStartRes()); addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end, viewport.getAlignment())); - vpRanges.setStartRes(seq.findIndex(startRes) - 1); + viewport.getRanges().setStartRes(seq.findIndex(startRes) - 1); viewport.firePropertyChange("alignment", null, viewport.getAlignment().getSequences()); @@ -2713,8 +2712,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /* * Create a new AlignmentPanel (with its own, new Viewport) */ - AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel, - true); + AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel); if (!copyAnnotation) { /* @@ -2868,21 +2866,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, alignPanel.getIdPanel().getIdCanvas() .setPreferredSize(alignPanel.calculateIdWidth()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } @Override public void idRightAlign_actionPerformed(ActionEvent e) { viewport.setRightAlignIds(idRightAlign.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } @Override public void centreColumnLabels_actionPerformed(ActionEvent e) { viewport.setCentreColumnLabels(centreColumnLabelsMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /* @@ -2915,7 +2913,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void colourTextMenuItem_actionPerformed(ActionEvent e) { viewport.setColourText(colourTextMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** @@ -2944,7 +2942,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void showAllColumns_actionPerformed(ActionEvent e) { viewport.showAllHiddenColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3048,7 +3046,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.expandColSelection(sg, false); viewport.hideAllSelectedSeqs(); viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3064,7 +3062,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { viewport.showAllHiddenColumns(); viewport.showAllHiddenSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3072,7 +3070,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void hideSelColumns_actionPerformed(ActionEvent e) { viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3093,7 +3091,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void scaleAbove_actionPerformed(ActionEvent e) { viewport.setScaleAboveWrapped(scaleAbove.isSelected()); - alignPanel.paintAlignment(true); + // TODO: do we actually need to update overview for scale above change ? + alignPanel.paintAlignment(true, false); } /** @@ -3106,7 +3105,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void scaleLeft_actionPerformed(ActionEvent e) { viewport.setScaleLeftWrapped(scaleLeft.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3119,7 +3118,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void scaleRight_actionPerformed(ActionEvent e) { viewport.setScaleRightWrapped(scaleRight.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3132,7 +3131,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void viewBoxesMenuItem_actionPerformed(ActionEvent e) { viewport.setShowBoxes(viewBoxesMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** @@ -3145,7 +3144,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void viewTextMenuItem_actionPerformed(ActionEvent e) { viewport.setShowText(viewTextMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** @@ -3158,7 +3157,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void renderGapsMenuItem_actionPerformed(ActionEvent e) { viewport.setRenderGaps(renderGapsMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } public FeatureSettings featureSettings; @@ -3196,7 +3195,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void showSeqFeatures_actionPerformed(ActionEvent evt) { viewport.setShowSequenceFeatures(showSeqFeatures.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } /** @@ -3353,7 +3352,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.setGlobalColourScheme(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } /** @@ -3438,7 +3437,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment().getSequenceAt(0)); addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3454,7 +3453,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignmentSorter.sortByID(viewport.getAlignment()); addHistoryItem( new OrderCommand("ID Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3470,7 +3469,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignmentSorter.sortByLength(viewport.getAlignment()); addHistoryItem(new OrderCommand("Length Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3487,7 +3486,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addHistoryItem(new OrderCommand("Group Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3644,7 +3643,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } }); } @@ -3673,7 +3672,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment());// ,viewport.getSelectionGroup()); addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } }); } @@ -3788,7 +3787,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addHistoryItem(new OrderCommand(undoname, oldOrder, viewport.getAlignment())); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); return true; } @@ -4503,7 +4502,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, assocfiles++; } } - alignPanel.paintAlignment(true); + // TODO: do we need to update overview ? only if features are + // shown I guess + alignPanel.paintAlignment(true, false); } } } @@ -4650,7 +4651,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { if (parseFeaturesFile(file, sourceType)) { - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } else @@ -4665,7 +4666,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, alignPanel.adjustAnnotationHeight(); viewport.updateSequenceIdColours(); buildSortByAnnotationScoresMenu(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } catch (Exception ex) { @@ -5190,7 +5191,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void showUnconservedMenuItem_actionPerformed(ActionEvent e) { viewport.setShowUnconserved(showNonconservedMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /* @@ -5279,7 +5280,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -5291,7 +5292,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment().setSeqrep(null); PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -5381,7 +5382,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder()); this.alignPanel.av .setShowAutocalculatedAbove(isShowAutoCalculatedAbove()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index c22a37d..4d09084 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -76,8 +76,6 @@ public class AlignViewport extends AlignmentViewport { Font font; - TreeModel currentTree = null; - boolean cursorMode = false; boolean antiAlias = false; @@ -448,27 +446,6 @@ public class AlignViewport extends AlignmentViewport } /** - * DOCUMENT ME! - * - * @param tree - * DOCUMENT ME! - */ - public void setCurrentTree(TreeModel tree) - { - currentTree = tree; - } - - /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public TreeModel getCurrentTree() - { - return currentTree; - } - - /** * returns the visible column regions of the alignment * * @param selectedRegionOnly @@ -606,58 +583,6 @@ public class AlignViewport extends AlignmentViewport .getStructureSelectionManager(Desktop.instance); } - /** - * - * @param pdbEntries - * @return an array of SequenceI arrays, one for each PDBEntry, listing which - * sequences in the alignment hold a reference to it - */ - public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries) - { - List seqvectors = new ArrayList(); - for (PDBEntry pdb : pdbEntries) - { - List choosenSeqs = new ArrayList(); - for (SequenceI sq : alignment.getSequences()) - { - Vector pdbRefEntries = sq.getDatasetSequence() - .getAllPDBEntries(); - if (pdbRefEntries == null) - { - continue; - } - for (PDBEntry pdbRefEntry : pdbRefEntries) - { - if (pdbRefEntry.getId().equals(pdb.getId())) - { - if (pdbRefEntry.getChainCode() != null - && pdb.getChainCode() != null) - { - if (pdbRefEntry.getChainCode().equalsIgnoreCase( - pdb.getChainCode()) && !choosenSeqs.contains(sq)) - { - choosenSeqs.add(sq); - continue; - } - } - else - { - if (!choosenSeqs.contains(sq)) - { - choosenSeqs.add(sq); - continue; - } - } - - } - } - } - seqvectors - .add(choosenSeqs.toArray(new SequenceI[choosenSeqs.size()])); - } - return seqvectors.toArray(new SequenceI[seqvectors.size()][]); - } - @Override public boolean isNormaliseSequenceLogo() { @@ -1110,5 +1035,4 @@ public class AlignViewport extends AlignmentViewport } fr.setTransparency(featureSettings.getTransparency()); } - } diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index c700635..3a1dbe8 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -76,8 +76,6 @@ public class AlignmentPanel extends GAlignmentPanel implements { public AlignViewport av; - ViewportRanges vpRanges; - OverviewPanel overviewPanel; private SeqPanel seqPanel; @@ -97,9 +95,6 @@ public class AlignmentPanel extends GAlignmentPanel implements private AnnotationLabels alabels; - // this value is set false when selection area being dragged - boolean fastPaint = true; - private int hextent = 0; private int vextent = 0; @@ -124,7 +119,6 @@ public class AlignmentPanel extends GAlignmentPanel implements { alignFrame = af; this.av = av; - vpRanges = av.getRanges(); setSeqPanel(new SeqPanel(av, this)); setIdPanel(new IdPanel(av, this)); @@ -156,11 +150,12 @@ public class AlignmentPanel extends GAlignmentPanel implements // reset the viewport ranges when the alignment panel is resized // in particular, this initialises the end residue value when Jalview // is initialised + ViewportRanges ranges = av.getRanges(); if (av.getWrapAlignment()) { int widthInRes = getSeqPanel().seqCanvas.getWrappedCanvasWidth( getSeqPanel().seqCanvas.getWidth()); - vpRanges.setViewportWidth(widthInRes); + ranges.setViewportWidth(widthInRes); } else { @@ -169,8 +164,8 @@ public class AlignmentPanel extends GAlignmentPanel implements int heightInSeq = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); - vpRanges.setViewportWidth(widthInRes); - vpRanges.setViewportHeight(heightInSeq); + ranges.setViewportWidth(widthInRes); + ranges.setViewportHeight(heightInSeq); } } @@ -214,7 +209,8 @@ public class AlignmentPanel extends GAlignmentPanel implements alignFrame.updateEditMenuBar(); - paintAlignment(true); + // no idea if we need to update structure + paintAlignment(true, true); } @@ -379,6 +375,7 @@ public class AlignmentPanel extends GAlignmentPanel implements int verticalOffset, boolean redrawOverview, boolean centre) { int startv, endv, starts, ends; + ViewportRanges ranges = av.getRanges(); if (results == null || results.isEmpty() || av == null || av.getAlignment() == null) @@ -406,7 +403,7 @@ public class AlignmentPanel extends GAlignmentPanel implements */ if (centre) { - int offset = (vpRanges.getEndRes() - vpRanges.getStartRes() + 1) / 2 - 1; + int offset = (ranges.getEndRes() - ranges.getStartRes() + 1) / 2 - 1; start = Math.max(start - offset, 0); end = end + offset - 1; } @@ -442,33 +439,33 @@ public class AlignmentPanel extends GAlignmentPanel implements if (!av.getWrapAlignment()) { - if ((startv = vpRanges.getStartRes()) >= start) + if ((startv = ranges.getStartRes()) >= start) { /* * Scroll left to make start of search results visible */ setScrollValues(start, seqIndex); } - else if ((endv = vpRanges.getEndRes()) <= end) + else if ((endv = ranges.getEndRes()) <= end) { /* * Scroll right to make end of search results visible */ setScrollValues(startv + end - endv, seqIndex); } - else if ((starts = vpRanges.getStartSeq()) > seqIndex) + else if ((starts = ranges.getStartSeq()) > seqIndex) { /* * Scroll up to make start of search results visible */ - setScrollValues(vpRanges.getStartRes(), seqIndex); + setScrollValues(ranges.getStartRes(), seqIndex); } - else if ((ends = vpRanges.getEndSeq()) <= seqIndex) + else if ((ends = ranges.getEndSeq()) <= seqIndex) { /* * Scroll down to make end of search results visible */ - setScrollValues(vpRanges.getStartRes(), starts + seqIndex - ends + setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1); } /* @@ -478,10 +475,10 @@ public class AlignmentPanel extends GAlignmentPanel implements } else { - scrollNeeded = vpRanges.scrollToWrappedVisible(start); + scrollNeeded = ranges.scrollToWrappedVisible(start); } - paintAlignment(redrawOverview); + paintAlignment(redrawOverview, false); return scrollNeeded; } @@ -538,7 +535,9 @@ public class AlignmentPanel extends GAlignmentPanel implements } validateAnnotationDimensions(true); addNotify(); - paintAlignment(true); + // TODO: many places call this method and also paintAlignment with various + // different settings. this means multiple redraws are triggered... + paintAlignment(true, false); } /** @@ -605,7 +604,8 @@ public class AlignmentPanel extends GAlignmentPanel implements fontChanged(); setAnnotationVisible(av.isShowAnnotation()); boolean wrap = av.getWrapAlignment(); - vpRanges.setStartSeq(0); + ViewportRanges ranges = av.getRanges(); + ranges.setStartSeq(0); scalePanelHolder.setVisible(!wrap); hscroll.setVisible(!wrap); idwidthAdjuster.setVisible(!wrap); @@ -628,16 +628,16 @@ public class AlignmentPanel extends GAlignmentPanel implements { int widthInRes = getSeqPanel().seqCanvas .getWrappedCanvasWidth(canvasWidth); - vpRanges.setViewportWidth(widthInRes); + ranges.setViewportWidth(widthInRes); } else { - int widthInRes = (canvasWidth / av.getCharWidth()) - 1; + int widthInRes = (canvasWidth / av.getCharWidth()); int heightInSeq = (getSeqPanel().seqCanvas.getHeight() - / av.getCharHeight()) - 1; + / av.getCharHeight()); - vpRanges.setViewportWidth(widthInRes); - vpRanges.setViewportHeight(heightInSeq); + ranges.setViewportWidth(widthInRes); + ranges.setViewportHeight(heightInSeq); } } @@ -736,10 +736,12 @@ public class AlignmentPanel extends GAlignmentPanel implements return; } + ViewportRanges ranges = av.getRanges(); + if (evt.getSource() == hscroll) { - int oldX = vpRanges.getStartRes(); - int oldwidth = vpRanges.getViewportWidth(); + int oldX = ranges.getStartRes(); + int oldwidth = ranges.getViewportWidth(); int x = hscroll.getValue(); int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); @@ -750,12 +752,12 @@ public class AlignmentPanel extends GAlignmentPanel implements { return; } - vpRanges.setViewportStartAndWidth(x, width); + ranges.setViewportStartAndWidth(x, width); } else if (evt.getSource() == vscroll) { - int oldY = vpRanges.getStartSeq(); - int oldheight = vpRanges.getViewportHeight(); + int oldY = ranges.getStartSeq(); + int oldheight = ranges.getViewportHeight(); int y = vscroll.getValue(); int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); @@ -766,12 +768,9 @@ public class AlignmentPanel extends GAlignmentPanel implements { return; } - vpRanges.setViewportStartAndHeight(y, height); - } - if (!fastPaint) - { - repaint(); + ranges.setViewportStartAndHeight(y, height); } + repaint(); } /** @@ -786,6 +785,8 @@ public class AlignmentPanel extends GAlignmentPanel implements { return; // no horizontal scroll when wrapped } + final ViewportRanges ranges = av.getRanges(); + if (evt.getSource() == vscroll) { int newY = vscroll.getValue(); @@ -795,8 +796,8 @@ public class AlignmentPanel extends GAlignmentPanel implements * this prevents infinite recursion of events when the scroll/viewport * ranges values are the same */ - int oldX = vpRanges.getStartRes(); - int oldY = vpRanges.getWrappedScrollPosition(oldX); + int oldX = ranges.getStartRes(); + int oldY = ranges.getWrappedScrollPosition(oldX); if (oldY == newY) { return; @@ -806,9 +807,9 @@ public class AlignmentPanel extends GAlignmentPanel implements /* * limit page up/down to one width's worth of positions */ - int rowSize = vpRanges.getViewportWidth(); + int rowSize = ranges.getViewportWidth(); int newX = newY > oldY ? oldX + rowSize : oldX - rowSize; - vpRanges.setViewportStartAndWidth(Math.max(0, newX), rowSize); + ranges.setViewportStartAndWidth(Math.max(0, newX), rowSize); } } else @@ -829,20 +830,20 @@ public class AlignmentPanel extends GAlignmentPanel implements "Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences"); // scroll to start of panel - vpRanges.setStartRes(0); - vpRanges.setStartSeq(0); + ranges.setStartRes(0); + ranges.setStartSeq(0); } }); } repaint(); } - /** - * Repaint the alignment including the annotations and overview panels (if - * shown). + /* (non-Javadoc) + * @see jalview.api.AlignmentViewPanel#paintAlignment(boolean) */ @Override - public void paintAlignment(boolean updateOverview) + public void paintAlignment(boolean updateOverview, + boolean updateStructures) { final AnnotationSorter sorter = new AnnotationSorter(getAlignment(), av.isShowAutocalculatedAbove()); @@ -850,10 +851,12 @@ public class AlignmentPanel extends GAlignmentPanel implements av.getSortAnnotationsBy()); repaint(); - if (updateOverview) + if (updateStructures) { - // TODO: determine if this paintAlignment changed structure colours av.getStructureSelectionManager().sequenceColoursChanged(this); + } + if (updateOverview) + { if (overviewPanel != null) { @@ -881,7 +884,8 @@ public class AlignmentPanel extends GAlignmentPanel implements /* * set scroll bar positions */ - setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq()); + ViewportRanges ranges = av.getRanges(); + setScrollValues(ranges.getStartRes(), ranges.getStartSeq()); } /** @@ -893,8 +897,9 @@ public class AlignmentPanel extends GAlignmentPanel implements */ private void setScrollingForWrappedPanel(int topLeftColumn) { - int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn); - int maxScroll = vpRanges.getWrappedMaxScroll(topLeftColumn); + ViewportRanges ranges = av.getRanges(); + int scrollPosition = ranges.getWrappedScrollPosition(topLeftColumn); + int maxScroll = ranges.getWrappedMaxScroll(topLeftColumn); /* * a scrollbar's value can be set to at most (maximum-extent) @@ -1605,13 +1610,14 @@ public class AlignmentPanel extends GAlignmentPanel implements if (annotationPanel != null) { annotationPanel.dispose(); + annotationPanel = null; } if (av != null) { av.removePropertyChangeListener(propertyChangeListener); - jalview.structure.StructureSelectionManager ssm = av - .getStructureSelectionManager(); + propertyChangeListener = null; + StructureSelectionManager ssm = av.getStructureSelectionManager(); ssm.removeStructureViewerListener(getSeqPanel(), null); ssm.removeSelectionListener(getSeqPanel()); ssm.removeCommandListener(av); @@ -1634,9 +1640,15 @@ public class AlignmentPanel extends GAlignmentPanel implements */ protected void closeChildFrames() { + if (overviewPanel != null) + { + overviewPanel.dispose(); + overviewPanel = null; + } if (calculationDialog != null) { calculationDialog.closeFrame(); + calculationDialog = null; } } @@ -1866,7 +1878,7 @@ public class AlignmentPanel extends GAlignmentPanel implements if (adjustHeight) { // sort, repaint, update overview - paintAlignment(true); + paintAlignment(true, false); } else { @@ -1883,8 +1895,9 @@ public class AlignmentPanel extends GAlignmentPanel implements public void propertyChange(PropertyChangeEvent evt) { // update this panel's scroll values based on the new viewport ranges values - int x = vpRanges.getStartRes(); - int y = vpRanges.getStartSeq(); + ViewportRanges ranges = av.getRanges(); + int x = ranges.getStartRes(); + int y = ranges.getStartSeq(); setScrollValues(x, y); // now update any complementary alignment (its viewport ranges object diff --git a/src/jalview/gui/AnnotationChooser.java b/src/jalview/gui/AnnotationChooser.java index 26796de..84883d7 100644 --- a/src/jalview/gui/AnnotationChooser.java +++ b/src/jalview/gui/AnnotationChooser.java @@ -82,7 +82,7 @@ public class AnnotationChooser extends JPanel private boolean applyToUnselectedSequences; // currently selected 'annotation type' checkboxes - private Map selectedTypes = new HashMap(); + private Map selectedTypes = new HashMap<>(); /** * Constructor. @@ -202,7 +202,7 @@ public class AnnotationChooser extends JPanel // this.ap.alabels.setSize(this.ap.alabels.getSize().width, // this.ap.annotationPanel.getSize().height); // this.ap.validate(); - this.ap.paintAlignment(true); + this.ap.paintAlignment(true, false); } /** @@ -233,7 +233,7 @@ public class AnnotationChooser extends JPanel // this.ap.alabels.setSize(this.ap.alabels.getSize().width, // this.ap.annotationPanel.getSize().height); // this.ap.validate(); - this.ap.paintAlignment(true); + this.ap.paintAlignment(true, false); } /** @@ -251,7 +251,7 @@ public class AnnotationChooser extends JPanel this.ap.updateAnnotation(); // this.ap.annotationPanel.adjustPanelHeight(); - this.ap.paintAlignment(true); + this.ap.paintAlignment(true, false); } /** @@ -356,7 +356,7 @@ public class AnnotationChooser extends JPanel public static List getAnnotationTypes(AlignmentI alignment, boolean sequenceSpecificOnly) { - List result = new ArrayList(); + List result = new ArrayList<>(); for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation()) { if (!sequenceSpecificOnly || aa.sequenceRef != null) diff --git a/src/jalview/gui/AnnotationColourChooser.java b/src/jalview/gui/AnnotationColourChooser.java index 8d123bb..153f70c 100644 --- a/src/jalview/gui/AnnotationColourChooser.java +++ b/src/jalview/gui/AnnotationColourChooser.java @@ -79,7 +79,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter oldcs = av.getGlobalColourScheme(); if (av.getAlignment().getGroups() != null) { - oldgroupColours = new Hashtable(); + oldgroupColours = new Hashtable<>(); for (SequenceGroup sg : ap.av.getAlignment().getGroups()) { if (sg.getColourScheme() != null) @@ -122,7 +122,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter } Vector annotItems = getAnnotationItems( seqAssociated.isSelected()); - annotations = new JComboBox(annotItems); + annotations = new JComboBox<>(annotItems); populateThresholdComboBox(threshold); @@ -341,7 +341,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter getCurrentAnnotation().threshold.value = slider.getValue() / 1000f; propagateSeqAssociatedThreshold(updateAllAnnotation, getCurrentAnnotation()); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -415,12 +415,9 @@ public class AnnotationColourChooser extends AnnotationRowFilter colorAlignmentContaining(getCurrentAnnotation(), selectedThresholdItem); ap.alignmentChanged(); - // ensure all associated views (overviews, structures, etc) are notified of - // updated colours. - ap.paintAlignment(true); } - protected boolean colorAlignmentContaining(AlignmentAnnotation currentAnn, + protected void colorAlignmentContaining(AlignmentAnnotation currentAnn, int selectedThresholdOption) { @@ -460,7 +457,6 @@ public class AnnotationColourChooser extends AnnotationRowFilter acg.getInstance(sg, ap.av.getHiddenRepSequences())); } } - return false; } } diff --git a/src/jalview/gui/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java index 84b2c6f..020e027 100644 --- a/src/jalview/gui/AnnotationColumnChooser.java +++ b/src/jalview/gui/AnnotationColumnChooser.java @@ -254,7 +254,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter av.getAlignment().setHiddenColumns(oldHidden); } av.sendSelection(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } @@ -267,7 +267,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter updateView(); propagateSeqAssociatedThreshold(updateAllAnnotation, getCurrentAnnotation()); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -391,7 +391,8 @@ public class AnnotationColumnChooser extends AnnotationRowFilter av.getColumnSelection().filterAnnotations( getCurrentAnnotation().annotations, filterParams); - if (getActionOption() == ACTION_OPTION_HIDE) + boolean hideCols = getActionOption() == ACTION_OPTION_HIDE; + if (hideCols) { av.hideSelectedColumns(); } @@ -399,7 +400,8 @@ public class AnnotationColumnChooser extends AnnotationRowFilter filterParams = null; av.setAnnotationColumnSelectionState(this); - ap.paintAlignment(true); + // only update overview and structures if columns were hidden + ap.paintAlignment(hideCols, hideCols); } public HiddenColumns getOldHiddenColumns() diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index d07cae2..b94a615 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -705,7 +705,7 @@ public class AnnotationLabels extends JPanel d = ap.annotationSpaceFillerHolder.getPreferredSize(); ap.annotationSpaceFillerHolder .setPreferredSize(new Dimension(d.width, d.height - dif)); - ap.paintAlignment(true); + ap.paintAlignment(true, false); } ap.addNotify(); @@ -855,7 +855,7 @@ public class AnnotationLabels extends JPanel } } - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } @@ -912,7 +912,7 @@ public class AnnotationLabels extends JPanel sg.addSequence(aa[selectedRow].sequenceRef, false); } ap.av.setSelectionGroup(sg); - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index be8f5f6..12d1369 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -670,7 +670,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } graphStretchY = evt.getY(); adjustPanelHeight(); - ap.paintAlignment(true); + ap.paintAlignment(false, false); } else { diff --git a/src/jalview/gui/AnnotationRowFilter.java b/src/jalview/gui/AnnotationRowFilter.java index 8b2486f..71ad6a5 100644 --- a/src/jalview/gui/AnnotationRowFilter.java +++ b/src/jalview/gui/AnnotationRowFilter.java @@ -80,7 +80,7 @@ public abstract class AnnotationRowFilter extends JPanel */ protected boolean sliderDragging = false; - protected JComboBox threshold = new JComboBox(); + protected JComboBox threshold = new JComboBox<>(); protected JComboBox annotations; @@ -177,7 +177,6 @@ public abstract class AnnotationRowFilter extends JPanel sliderDragging = false; valueChanged(true); } - ap.paintAlignment(true); } }); } @@ -192,9 +191,9 @@ public abstract class AnnotationRowFilter extends JPanel */ public Vector getAnnotationItems(boolean isSeqAssociated) { - annotationLabels = new HashMap(); + annotationLabels = new HashMap<>(); - Vector list = new Vector(); + Vector list = new Vector<>(); int index = 1; int[] anmap = new int[av.getAlignment() .getAlignmentAnnotation().length]; @@ -271,7 +270,7 @@ public abstract class AnnotationRowFilter extends JPanel public void cancel_actionPerformed() { reset(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); try { frame.setClosed(true); @@ -413,6 +412,11 @@ public abstract class AnnotationRowFilter extends JPanel this.currentAnnotation = annotation; } + /** + * update associated view model and trigger any necessary repaints. + * + * @param updateAllAnnotation + */ protected abstract void valueChanged(boolean updateAllAnnotation); protected abstract void updateView(); diff --git a/src/jalview/gui/AppJmol.java b/src/jalview/gui/AppJmol.java index a4597d3..fef7451 100644 --- a/src/jalview/gui/AppJmol.java +++ b/src/jalview/gui/AppJmol.java @@ -157,6 +157,11 @@ public class AppJmol extends StructureViewerBase IProgressIndicator progressBar = null; + @Override + protected IProgressIndicator getIProgressIndicator() + { + return progressBar; + } /** * add a single PDB structure to a new or existing Jmol view * @@ -248,7 +253,7 @@ public class AppJmol extends StructureViewerBase @Override protected List getViewersFor(AlignmentPanel apanel) { - List result = new ArrayList(); + List result = new ArrayList<>(); JInternalFrame[] frames = Desktop.instance.getAllFrames(); for (JInternalFrame frame : frames) @@ -300,7 +305,7 @@ public class AppJmol extends StructureViewerBase @Override void showSelectedChains() { - Vector toshow = new Vector(); + Vector toshow = new Vector<>(); for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) @@ -489,7 +494,7 @@ public class AppJmol extends StructureViewerBase // todo - record which pdbids were successfully imported. StringBuilder errormsgs = new StringBuilder(); - List files = new ArrayList(); + List files = new ArrayList<>(); String pdbid = ""; try { diff --git a/src/jalview/gui/AppJmolBinding.java b/src/jalview/gui/AppJmolBinding.java index 9325172..724cec1 100644 --- a/src/jalview/gui/AppJmolBinding.java +++ b/src/jalview/gui/AppJmolBinding.java @@ -49,6 +49,12 @@ public class AppJmolBinding extends JalviewJmolBinding } @Override + protected IProgressIndicator getIProgressIndicator() + { + return appJmolWindow.progressBar; + } + + @Override public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment) { return new SequenceRenderer(((AlignmentPanel) alignment).av); diff --git a/src/jalview/gui/AquaInternalFrameManager.java b/src/jalview/gui/AquaInternalFrameManager.java new file mode 100644 index 0000000..ea809eb --- /dev/null +++ b/src/jalview/gui/AquaInternalFrameManager.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jalview.gui; + +import java.awt.Container; +import java.beans.PropertyVetoException; +import java.util.Vector; + +import javax.swing.DefaultDesktopManager; +import javax.swing.DesktopManager; +import javax.swing.JInternalFrame; + +/** + * Based on AquaInternalFrameManager + * + * DesktopManager implementation for Aqua + * + * Mac is more like Windows than it's like Motif/Basic + * + * From WindowsDesktopManager: + * + * This class implements a DesktopManager which more closely follows the MDI + * model than the DefaultDesktopManager. Unlike the DefaultDesktopManager + * policy, MDI requires that the selected and activated child frames are the + * same, and that that frame always be the top-most window. + *

    + * The maximized state is managed by the DesktopManager with MDI, instead of + * just being a property of the individual child frame. This means that if the + * currently selected window is maximized and another window is selected, that + * new window will be maximized. + * + * Downloaded from + * https://raw.githubusercontent.com/frohoff/jdk8u-jdk/master/src/macosx/classes/com/apple/laf/AquaInternalFrameManager.java + * + * Patch from Jim Procter - when the most recently opened frame is closed, + * correct behaviour is to go to the next most recent frame, rather than wrap + * around to the bottom of the window stack (as the original implementation + * does) + * + * @see com.sun.java.swing.plaf.windows.WindowsDesktopManager + */ +public class AquaInternalFrameManager extends DefaultDesktopManager +{ + // Variables + + /* The frame which is currently selected/activated. + * We store this value to enforce Mac's single-selection model. + */ + JInternalFrame fCurrentFrame; + + JInternalFrame fInitialFrame; + + /* The list of frames, sorted by order of creation. + * This list is necessary because by default the order of + * child frames in the JDesktopPane changes during frame + * activation (the activated frame is moved to index 0). + * We preserve the creation order so that "next" and "previous" + * frame actions make sense. + */ + Vector fChildFrames = new Vector<>(1); + + /** + * keep a reference to the original LAF manager so we can iconise/de-iconise + * correctly + */ + private DesktopManager ourManager; + + public AquaInternalFrameManager(DesktopManager desktopManager) + { + ourManager = desktopManager; + } + + @Override + public void closeFrame(final JInternalFrame f) + { + if (f == fCurrentFrame) + { + boolean mostRecentFrame = fChildFrames + .indexOf(f) == fChildFrames.size() - 1; + if (!mostRecentFrame) + { + activateNextFrame(); + } + else + { + activatePreviousFrame(); + } + } + fChildFrames.removeElement(f); + super.closeFrame(f); + } + + @Override + public void deiconifyFrame(final JInternalFrame f) + { + JInternalFrame.JDesktopIcon desktopIcon; + + desktopIcon = f.getDesktopIcon(); + // If the icon moved, move the frame to that spot before expanding it + // reshape does delta checks for us + f.reshape(desktopIcon.getX(), desktopIcon.getY(), f.getWidth(), + f.getHeight()); + ourManager.deiconifyFrame(f); + } + + void addIcon(final Container c, + final JInternalFrame.JDesktopIcon desktopIcon) + { + c.add(desktopIcon); + } + + /** + * Removes the frame from its parent and adds its desktopIcon to the parent. + */ + @Override + public void iconifyFrame(final JInternalFrame f) + { + ourManager.iconifyFrame(f); + } + + // WindowsDesktopManager code + @Override + public void activateFrame(final JInternalFrame f) + { + try + { + if (f != null) + { + super.activateFrame(f); + } + + // If this is the first activation, add to child list. + if (fChildFrames.indexOf(f) == -1) + { + fChildFrames.addElement(f); + } + + if (fCurrentFrame != null && f != fCurrentFrame) + { + if (fCurrentFrame.isSelected()) + { + fCurrentFrame.setSelected(false); + } + } + + if (f != null && !f.isSelected()) + { + f.setSelected(true); + } + + fCurrentFrame = f; + } catch (final PropertyVetoException e) + { + } + } + + private void switchFrame(final boolean next) + { + if (fCurrentFrame == null) + { + // initialize first frame we find + if (fInitialFrame != null) + { + activateFrame(fInitialFrame); + } + return; + } + + final int count = fChildFrames.size(); + if (count <= 1) + { + // No other child frames. + return; + } + + final int currentIndex = fChildFrames.indexOf(fCurrentFrame); + if (currentIndex == -1) + { + // the "current frame" is no longer in the list + fCurrentFrame = null; + return; + } + + int nextIndex; + if (next) + { + nextIndex = currentIndex + 1; + if (nextIndex == count) + { + nextIndex = 0; + } + } + else + { + nextIndex = currentIndex - 1; + if (nextIndex == -1) + { + nextIndex = count - 1; + } + } + final JInternalFrame f = fChildFrames.elementAt(nextIndex); + activateFrame(f); + fCurrentFrame = f; + } + + /** + * Activate the next child JInternalFrame, as determined by the frames' + * Z-order. If there is only one child frame, it remains activated. If there + * are no child frames, nothing happens. + */ + public void activateNextFrame() + { + switchFrame(true); + } + + /** + * same as above but will activate a frame if none have been selected + */ + public void activateNextFrame(final JInternalFrame f) + { + fInitialFrame = f; + switchFrame(true); + } + + /** + * Activate the previous child JInternalFrame, as determined by the frames' + * Z-order. If there is only one child frame, it remains activated. If there + * are no child frames, nothing happens. + */ + public void activatePreviousFrame() + { + switchFrame(false); + } +} \ No newline at end of file diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index a9f3966..e403dba 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -105,6 +105,11 @@ public class CalculationChooser extends JPanel List tips = new ArrayList(); + /* + * the most recently opened PCA results panel + */ + private PCAPanel pcaPanel; + /** * Constructor * @@ -534,7 +539,7 @@ public class CalculationChooser extends JPanel JvOptionPane.WARNING_MESSAGE); return; } - new PCAPanel(af.alignPanel, modelName, params); + pcaPanel = new PCAPanel(af.alignPanel, modelName, params); } /** @@ -592,4 +597,9 @@ public class CalculationChooser extends JPanel { } } + + public PCAPanel getPcaPanel() + { + return pcaPanel; + } } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index ba360af..89de2e8 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -358,7 +358,7 @@ public class ChimeraViewFrame extends StructureViewerBase @Override protected List getViewersFor(AlignmentPanel ap) { - List result = new ArrayList(); + List result = new ArrayList<>(); JInternalFrame[] frames = Desktop.instance.getAllFrames(); for (JInternalFrame frame : frames) @@ -414,7 +414,7 @@ public class ChimeraViewFrame extends StructureViewerBase @Override void showSelectedChains() { - List toshow = new ArrayList(); + List toshow = new ArrayList<>(); for (int i = 0; i < chainMenu.getItemCount(); i++) { if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) @@ -484,8 +484,8 @@ public class ChimeraViewFrame extends StructureViewerBase // todo - record which pdbids were successfully imported. StringBuilder errormsgs = new StringBuilder(128); StringBuilder files = new StringBuilder(128); - List filePDB = new ArrayList(); - List filePDBpos = new ArrayList(); + List filePDB = new ArrayList<>(); + List filePDBpos = new ArrayList<>(); PDBEntry thePdbEntry = null; StructureFile pdb = null; try @@ -598,9 +598,12 @@ public class ChimeraViewFrame extends StructureViewerBase stopProgressBar("", startTime); } // Explicitly map to the filename used by Chimera ; + pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos], - jmb.getChains()[pos], pe.getFile(), protocol); + jmb.getChains()[pos], pe.getFile(), protocol, + progressBar); stashFoundChains(pdb, pe.getFile()); + } catch (OutOfMemoryError oomerror) { new OOMWarning( @@ -658,7 +661,7 @@ public class ChimeraViewFrame extends StructureViewerBase /** * Fetch PDB data and save to a local file. Returns the full path to the file, - * or null if fetch fails. + * or null if fetch fails. TODO: refactor to common with Jmol ? duplication * * @param processingEntry * @return @@ -891,4 +894,10 @@ public class ChimeraViewFrame extends StructureViewerBase } return reply; } + + @Override + protected IProgressIndicator getIProgressIndicator() + { + return progressBar; + } } diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 1f8983f..128481c 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -68,8 +68,6 @@ import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -361,7 +359,10 @@ public class Desktop extends jalview.jbgui.GDesktop desktop.setDesktopManager( new MyDesktopManager( (Platform.isWindows() ? new DefaultDesktopManager() - : desktop.getDesktopManager()))); + : Platform.isAMac() + ? new AquaInternalFrameManager( + desktop.getDesktopManager()) + : desktop.getDesktopManager()))); Rectangle dims = getLastKnownDimensions(""); if (dims != null) @@ -431,24 +432,6 @@ public class Desktop extends jalview.jbgui.GDesktop }); desktop.addMouseListener(ma); - this.addFocusListener(new FocusListener() - { - - @Override - public void focusLost(FocusEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void focusGained(FocusEvent e) - { - Cache.log.debug("Relaying windows after focus gain"); - // make sure that we sort windows properly after we gain focus - instance.relayerWindows(); - } - }); this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this)); // Spawn a thread that shows the splashscreen SwingUtilities.invokeLater(new Runnable() @@ -886,6 +869,10 @@ public class Desktop extends jalview.jbgui.GDesktop JInternalFrame itf = desktop.getSelectedFrame(); if (itf != null) { + if (itf instanceof AlignFrame) + { + Jalview.setCurrentAlignFrame((AlignFrame) itf); + } itf.requestFocus(); } } @@ -912,15 +899,7 @@ public class Desktop extends jalview.jbgui.GDesktop menuItem.removeActionListener(menuItem.getActionListeners()[0]); } windowMenu.remove(menuItem); - JInternalFrame itf = desktop.getSelectedFrame(); - if (itf != null) - { - itf.requestFocus(); - if (itf instanceof AlignFrame) - { - Jalview.setCurrentAlignFrame((AlignFrame) itf); - } - } + System.gc(); }; }); @@ -2359,7 +2338,7 @@ public class Desktop extends jalview.jbgui.GDesktop { String link = li.next(); if (link.contains(SEQUENCE_ID) - && !link.equals(UrlConstants.DEFAULT_STRING)) + && !UrlConstants.isDefaultString(link)) { check = true; int barPos = link.indexOf("|"); @@ -2512,14 +2491,6 @@ public class Desktop extends jalview.jbgui.GDesktop } } - /** - * fixes stacking order after a modal dialog to ensure windows that should be - * on top actually are - */ - public void relayerWindows() - { - - } /** * Accessor method to quickly get all the AlignmentFrames loaded. diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java index b27328d..d8db546 100644 --- a/src/jalview/gui/FeatureColourChooser.java +++ b/src/jalview/gui/FeatureColourChooser.java @@ -147,7 +147,7 @@ public class FeatureColourChooser extends JalviewDialog */ if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } }); @@ -396,9 +396,9 @@ public class FeatureColourChooser extends JalviewDialog * feature type, and repaints the alignment, and optionally the Overview * and/or structure viewer if open * - * @param updateOverview + * @param updateStructsAndOverview */ - void changeColour(boolean updateOverview) + void changeColour(boolean updateStructsAndOverview) { // Check if combobox is still adjusting if (adjusting) @@ -507,7 +507,7 @@ public class FeatureColourChooser extends JalviewDialog } fr.setColour(type, acg); cs = acg; - ap.paintAlignment(updateOverview); + ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview); } @Override @@ -539,7 +539,7 @@ public class FeatureColourChooser extends JalviewDialog void reset() { fr.setColour(type, oldcs); - ap.paintAlignment(true); + ap.paintAlignment(true, true); cs = null; } @@ -565,7 +565,7 @@ public class FeatureColourChooser extends JalviewDialog /* * force repaint of any Overview window or structure */ - ap.paintAlignment(true); + ap.paintAlignment(true, true); } catch (NumberFormatException ex) { } diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java index 17f5a71..9c4b009 100644 --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@ -243,7 +243,7 @@ public class FeatureRenderer JPanel choosePanel = new JPanel(); choosePanel.add(new JLabel( MessageManager.getString("label.select_feature") + ":")); - final JComboBox overlaps = new JComboBox(); + final JComboBox overlaps = new JComboBox<>(); List added = new ArrayList<>(); for (SequenceFeature sf : features) { @@ -487,7 +487,7 @@ public class FeatureRenderer featuresAdded(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); return true; } @@ -497,7 +497,7 @@ public class FeatureRenderer } } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); return true; } diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index 0963b31..3f1d9c7 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -131,6 +131,11 @@ public class FeatureSettings extends JPanel private static final int MIN_WIDTH = 400; private static final int MIN_HEIGHT = 400; + + /** + * when true, constructor is still executing - so ignore UI events + */ + protected volatile boolean inConstruction = true; /** * Constructor @@ -303,6 +308,7 @@ public class FeatureSettings extends JPanel }; }); frame.setLayer(JLayeredPane.PALETTE_LAYER); + inConstruction = false; } protected void popupSort(final int selectedRow, final String type, @@ -485,7 +491,7 @@ public class FeatureSettings extends JPanel @Override synchronized public void discoverAllFeatureData() { - Set allGroups = new HashSet(); + Set allGroups = new HashSet<>(); AlignmentI alignment = af.getViewport().getAlignment(); for (int i = 0; i < alignment.getHeight(); i++) @@ -535,13 +541,8 @@ public class FeatureSettings extends JPanel public void itemStateChanged(ItemEvent evt) { fr.setGroupVisibility(check.getText(), check.isSelected()); - af.alignPanel.getSeqPanel().seqCanvas.repaint(); - if (af.alignPanel.overviewPanel != null) - { - af.alignPanel.overviewPanel.updateOverviewImage(); - } - resetTable(new String[] { grp }); + af.alignPanel.paintAlignment(true, true); } }); groupPanel.add(check); @@ -557,12 +558,12 @@ public class FeatureSettings extends JPanel return; } resettingTable = true; - typeWidth = new Hashtable(); + typeWidth = new Hashtable<>(); // TODO: change avWidth calculation to 'per-sequence' average and use long // rather than float - Set displayableTypes = new HashSet(); - Set foundGroups = new HashSet(); + Set displayableTypes = new HashSet<>(); + Set foundGroups = new HashSet<>(); /* * determine which feature types may be visible depending on @@ -578,7 +579,7 @@ public class FeatureSettings extends JPanel * and keep track of which groups are visible */ Set groups = seq.getFeatures().getFeatureGroups(true); - Set visibleGroups = new HashSet(); + Set visibleGroups = new HashSet<>(); for (String group : groups) { if (group == null || checkGroupState(group)) @@ -1058,7 +1059,7 @@ public class FeatureSettings extends JPanel { if (fr.setFeaturePriority(data, visibleNew)) { - af.alignPanel.paintAlignment(true); + af.alignPanel.paintAlignment(true, true); } } @@ -1237,8 +1238,11 @@ public class FeatureSettings extends JPanel @Override public void stateChanged(ChangeEvent evt) { - fr.setTransparency((100 - transparency.getValue()) / 100f); - af.alignPanel.paintAlignment(true); + if (!inConstruction) + { + fr.setTransparency((100 - transparency.getValue()) / 100f); + af.alignPanel.paintAlignment(true,true); + } } }); diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java index 6cddcca..f3c8e8f 100755 --- a/src/jalview/gui/FontChooser.java +++ b/src/jalview/gui/FontChooser.java @@ -183,7 +183,7 @@ public class FontChooser extends GFontChooser { ap.av.antiAlias = smoothFont.isSelected(); ap.getAnnotationPanel().image = null; - ap.paintAlignment(true); + ap.paintAlignment(true, false); if (ap.av.getCodingComplement() != null && ap.av.isProteinFontAsCdna()) { ((AlignViewport) ap.av @@ -235,7 +235,7 @@ public class FontChooser extends GFontChooser ap.av.setScaleProteinAsCdna(oldProteinScale); ap.av.setProteinFontAsCdna(oldMirrorFont); ap.av.antiAlias = oldSmoothFont; - ap.paintAlignment(true); + ap.fontChanged(); if (scaleAsCdna.isVisible() && scaleAsCdna.isEnabled()) { diff --git a/src/jalview/gui/IProgressIndicator.java b/src/jalview/gui/IProgressIndicator.java index 981e94c..35bd871 100644 --- a/src/jalview/gui/IProgressIndicator.java +++ b/src/jalview/gui/IProgressIndicator.java @@ -34,7 +34,8 @@ public interface IProgressIndicator * is removed with a second call with same ID. * * @param message - * - displayed message for operation + * - displayed message for operation. Please ensure message is + * internationalised. * @param id * - unique handle for this indicator */ diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index 3cc0ed3..1f2a3ad 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -138,7 +138,7 @@ public class IdPanel extends JPanel } lastid = seq; - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } /** @@ -154,7 +154,7 @@ public class IdPanel extends JPanel { av.getRanges().scrollRight(true); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(false); } @@ -165,7 +165,7 @@ public class IdPanel extends JPanel { av.getRanges().scrollRight(false); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(true); } @@ -313,7 +313,7 @@ public class IdPanel extends JPanel av.isSelectionGroupChanged(true); - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } /** @@ -507,7 +507,7 @@ public class IdPanel extends JPanel running = false; } - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); try { diff --git a/src/jalview/gui/IdwidthAdjuster.java b/src/jalview/gui/IdwidthAdjuster.java index 3c4107f..8400543 100755 --- a/src/jalview/gui/IdwidthAdjuster.java +++ b/src/jalview/gui/IdwidthAdjuster.java @@ -75,6 +75,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mousePressed(MouseEvent evt) { oldX = evt.getX(); @@ -86,6 +87,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseReleased(MouseEvent evt) { active = false; @@ -112,6 +114,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseEntered(MouseEvent evt) { active = true; @@ -124,6 +127,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseExited(MouseEvent evt) { active = false; @@ -136,6 +140,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseDragged(MouseEvent evt) { active = true; @@ -149,7 +154,7 @@ public class IdwidthAdjuster extends JPanel { viewport.setIdWidth(newWidth); - ap.paintAlignment(true); + ap.paintAlignment(true, false); } oldX = evt.getX(); @@ -161,6 +166,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseMoved(MouseEvent evt) { } @@ -171,6 +177,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseClicked(MouseEvent evt) { } @@ -181,6 +188,7 @@ public class IdwidthAdjuster extends JPanel * @param g * DOCUMENT ME! */ + @Override public void paintComponent(Graphics g) { g.setColor(Color.white); diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index b357234..4a15024 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -216,34 +216,6 @@ public class Jalview2XML } } - void clearSeqRefs() - { - if (_cleartables) - { - if (seqRefIds != null) - { - seqRefIds.clear(); - } - if (seqsToIds != null) - { - seqsToIds.clear(); - } - if (incompleteSeqs != null) - { - incompleteSeqs.clear(); - } - // seqRefIds = null; - // seqsToIds = null; - } - else - { - // do nothing - warn("clearSeqRefs called when _cleartables was not set. Doing nothing."); - // seqRefIds = new Hashtable(); - // seqsToIds = new IdentityHashMap(); - } - } - void initSeqRefs() { if (seqsToIds == null) @@ -1084,7 +1056,7 @@ public class Jalview2XML // SAVE TREES // ///////////////////////////////// - if (!storeDS && av.currentTree != null) + if (!storeDS && av.getCurrentTree() != null) { // FIND ANY ASSOCIATED TREES // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT @@ -1102,7 +1074,7 @@ public class Jalview2XML { Tree tree = new Tree(); tree.setTitle(tp.getTitle()); - tree.setCurrentTree((av.currentTree == tp.getTree())); + tree.setCurrentTree((av.getCurrentTree() == tp.getTree())); tree.setNewick(tp.getTree().print()); tree.setThreshold(tp.treeCanvas.threshold); @@ -4250,7 +4222,8 @@ public class Jalview2XML StructureData filedat = oldFiles.get(id); String pdbFile = filedat.getFilePath(); SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]); - binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE); + binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE, + null); binding.addSequenceForStructFile(pdbFile, seq); } // and add the AlignmentPanel's reference to the view panel @@ -5342,28 +5315,25 @@ public class Jalview2XML } - public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap, - boolean keepSeqRefs) + /** + * Provides a 'copy' of an alignment view (on action New View) by 'saving' the + * view as XML (but not to file), and then reloading it + * + * @param ap + * @return + */ + public AlignmentPanel copyAlignPanel(AlignmentPanel ap) { initSeqRefs(); JalviewModel jm = saveState(ap, null, null, null); - if (!keepSeqRefs) - { - clearSeqRefs(); - jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null); - } - else - { - uniqueSetSuffix = ""; - jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't - // overwrite the - // view we just - // copied - } + uniqueSetSuffix = ""; + jm.getJalviewModelSequence().getViewport(0).setId(null); + // we don't overwrite the view we just copied + if (this.frefedSequence == null) { - frefedSequence = new Vector(); + frefedSequence = new Vector(); } viewportsAdded.clear(); @@ -5383,32 +5353,8 @@ public class Jalview2XML return af.alignPanel; } - /** - * flag indicating if hashtables should be cleared on finalization TODO this - * flag may not be necessary - */ - private final boolean _cleartables = true; - private Hashtable jvids2vobj; - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - // really make sure we have no buried refs left. - if (_cleartables) - { - clearSeqRefs(); - } - this.seqRefIds = null; - this.seqsToIds = null; - super.finalize(); - } - private void warn(String msg) { warn(msg, null); diff --git a/src/jalview/gui/OverviewCanvas.java b/src/jalview/gui/OverviewCanvas.java index 7371eb5..2991889 100644 --- a/src/jalview/gui/OverviewCanvas.java +++ b/src/jalview/gui/OverviewCanvas.java @@ -183,6 +183,8 @@ public class OverviewCanvas extends JComponent @Override public void paintComponent(Graphics g) { + // super.paintComponent(g); + if (restart) { if (lastMiniMe == null) @@ -204,7 +206,8 @@ public class OverviewCanvas extends JComponent && ((getWidth() != od.getWidth()) || (getHeight() != od.getHeight()))) { - // if there is annotation, scale the alignment and annotation separately + // if there is annotation, scale the alignment and annotation + // separately if (od.getGraphHeight() > 0) { BufferedImage topImage = lastMiniMe.getSubimage(0, 0, @@ -235,25 +238,24 @@ public class OverviewCanvas extends JComponent od.setHeight(getHeight()); } - // scale lastMiniMe to the new size - g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); - // make sure the box is in the right place od.setBoxPosition(av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } - else // not a resize - { - // fall back to normal behaviour - g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); - } + // fall back to normal behaviour + g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); } - + else + { + g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); + } + // draw the box g.setColor(Color.red); od.drawBox(g); } + public void dispose() { dispose = true; diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 51d7a84..43b4310 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -40,8 +40,10 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; import javax.swing.JCheckBoxMenuItem; +import javax.swing.JInternalFrame; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; @@ -327,6 +329,22 @@ public class OverviewPanel extends JPanel * changed * */ + private void setBoxPositionOnly() + { + if (od != null) + { + int oldX = od.getBoxX(); + int oldY = od.getBoxY(); + int oldWidth = od.getBoxWidth(); + int oldHeight = od.getBoxHeight(); + od.setBoxPosition(av.getAlignment().getHiddenSequences(), + av.getAlignment().getHiddenColumns()); + repaint(oldX - 1, oldY - 1, oldWidth + 2, oldHeight + 2); + repaint(od.getBoxX(), od.getBoxY(), od.getBoxWidth(), + od.getBoxHeight()); + } + } + private void setBoxPosition() { if (od != null) @@ -340,7 +358,7 @@ public class OverviewPanel extends JPanel @Override public void propertyChange(PropertyChangeEvent evt) { - setBoxPosition(); + setBoxPositionOnly(); } /** @@ -350,8 +368,22 @@ public class OverviewPanel extends JPanel { try { - av.getRanges().removePropertyChangeListener(this); + if (av != null) + { + av.getRanges().removePropertyChangeListener(this); + } + oviewCanvas.dispose(); + + /* + * close the parent frame (which also removes it from the + * Desktop Windows menu) + */ + ((JInternalFrame) SwingUtilities.getAncestorOfClass( + JInternalFrame.class, (this))).setClosed(true); + } catch (PropertyVetoException e) + { + // ignore } finally { progressPanel = null; diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java index f861a7c..9f52d26 100644 --- a/src/jalview/gui/PCAPanel.java +++ b/src/jalview/gui/PCAPanel.java @@ -79,6 +79,8 @@ public class PCAPanel extends GPCAPanel int top = 0; + private boolean working; + /** * Creates a new PCAPanel object using default score model and parameters * @@ -234,6 +236,7 @@ public class PCAPanel extends GPCAPanel message = MessageManager.getString("label.pca_calculating"); } progress.setProgressBar(message, progId); + working = true; try { calcSettings.setEnabled(false); @@ -252,6 +255,7 @@ public class PCAPanel extends GPCAPanel } catch (OutOfMemoryError er) { new OOMWarning("calculating PCA", er); + working = false; return; } finally { @@ -266,6 +270,7 @@ public class PCAPanel extends GPCAPanel .getString("label.principal_component_analysis"), 475, 450); this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); } + working = false; } @Override @@ -788,4 +793,14 @@ public class PCAPanel extends GPCAPanel top = t; zCombobox.setSelectedIndex(2); } + + /** + * Answers true if PCA calculation is in progress, else false + * + * @return + */ + public boolean isWorking() + { + return working; + } } diff --git a/src/jalview/gui/PaintRefresher.java b/src/jalview/gui/PaintRefresher.java index d731e70..ced5544 100755 --- a/src/jalview/gui/PaintRefresher.java +++ b/src/jalview/gui/PaintRefresher.java @@ -26,9 +26,9 @@ import jalview.datamodel.SequenceI; import java.awt.Component; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; /** * Route datamodel/view update events for a sequence set to any display @@ -74,26 +74,21 @@ public class PaintRefresher */ public static void RemoveComponent(Component comp) { - List emptied = new ArrayList(); - for (Entry> registered : components.entrySet()) + if (components == null) { - String id = registered.getKey(); - List comps = components.get(id); + return; + } + + Iterator it = components.keySet().iterator(); + while (it.hasNext()) + { + List comps = components.get(it.next()); comps.remove(comp); if (comps.isEmpty()) { - emptied.add(id); + it.remove(); } } - - /* - * Remove now empty ids after the above (to avoid - * ConcurrentModificationException). - */ - for (String id : emptied) - { - components.remove(id); - } } public static void Refresh(Component source, String id) diff --git a/src/jalview/gui/PairwiseAlignPanel.java b/src/jalview/gui/PairwiseAlignPanel.java index f75407c..d081794 100755 --- a/src/jalview/gui/PairwiseAlignPanel.java +++ b/src/jalview/gui/PairwiseAlignPanel.java @@ -22,7 +22,8 @@ package jalview.gui; import jalview.analysis.AlignSeq; import jalview.datamodel.Alignment; -import jalview.datamodel.Sequence; +import jalview.datamodel.AlignmentView; +import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.jbgui.GPairwiseAlignPanel; import jalview.util.MessageManager; @@ -40,49 +41,56 @@ import java.util.Vector; public class PairwiseAlignPanel extends GPairwiseAlignPanel { + private static final String DASHES = "---------------------\n"; + AlignmentViewport av; - Vector sequences; + Vector sequences; /** * Creates a new PairwiseAlignPanel object. * - * @param av + * @param viewport * DOCUMENT ME! */ - public PairwiseAlignPanel(AlignmentViewport av) + public PairwiseAlignPanel(AlignmentViewport viewport) { super(); - this.av = av; + this.av = viewport; - sequences = new Vector(); + sequences = new Vector(); - SequenceI[] seqs; - String[] seqStrings = av.getViewAsString(true); + SequenceGroup selectionGroup = viewport.getSelectionGroup(); + boolean isSelection = selectionGroup != null + && selectionGroup.getSize() > 0; + AlignmentView view = viewport.getAlignmentView(isSelection); + // String[] seqStrings = viewport.getViewAsString(true); + String[] seqStrings = view.getSequenceStrings(viewport + .getGapCharacter()); - if (av.getSelectionGroup() == null) + SequenceI[] seqs; + if (isSelection) { - seqs = av.getAlignment().getSequencesArray(); + seqs = (SequenceI[]) view.getAlignmentAndHiddenColumns(viewport + .getGapCharacter())[0]; } else { - seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment()); + seqs = av.getAlignment().getSequencesArray(); } - String type = (av.getAlignment().isNucleotide()) ? AlignSeq.DNA + String type = (viewport.getAlignment().isNucleotide()) ? AlignSeq.DNA : AlignSeq.PEP; float[][] scores = new float[seqs.length][seqs.length]; - double totscore = 0; + double totscore = 0D; int count = seqs.length; - - Sequence seq; + boolean first = true; for (int i = 1; i < count; i++) { for (int j = 0; j < i; j++) { - AlignSeq as = new AlignSeq(seqs[i], seqStrings[i], seqs[j], seqStrings[j], type); @@ -94,9 +102,15 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel as.calcScoreMatrix(); as.traceAlignment(); + if (!first) + { + System.out.println(DASHES); + textarea.append(DASHES); + } + first = false; as.printAlignment(System.out); - scores[i][j] = (float) as.getMaxScore() - / (float) as.getASeq1().length; + scores[i][j] = as.getMaxScore() + / as.getASeq1().length; totscore = totscore + scores[i][j]; textarea.append(as.getOutput()); @@ -107,28 +121,53 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel if (count > 2) { - System.out.println( - "Pairwise alignment scaled similarity score matrix\n"); + printScoreMatrix(seqs, scores, totscore); + } + } - for (int i = 0; i < count; i++) - { - jalview.util.Format.print(System.out, "%s \n", - ("" + i) + " " + seqs[i].getName()); - } + /** + * Prints a matrix of seqi-seqj pairwise alignment scores to sysout + * + * @param seqs + * @param scores + * @param totscore + */ + protected void printScoreMatrix(SequenceI[] seqs, float[][] scores, + double totscore) + { + System.out + .println("Pairwise alignment scaled similarity score matrix\n"); - System.out.println("\n"); + for (int i = 0; i < seqs.length; i++) + { + System.out.println(String.format("%3d %s", i + 1, + seqs[i].getDisplayId(true))); + } + + /* + * table heading columns for sequences 1, 2, 3... + */ + System.out.print("\n "); + for (int i = 0; i < seqs.length; i++) + { + System.out.print(String.format("%7d", i + 1)); + } + System.out.println(); - for (int i = 0; i < count; i++) + for (int i = 0; i < seqs.length; i++) + { + System.out.print(String.format("%3d", i + 1)); + for (int j = 0; j < i; j++) { - for (int j = 0; j < i; j++) - { - jalview.util.Format.print(System.out, "%7.3f", - scores[i][j] / totscore); - } + /* + * as a fraction of tot score, outputs are 0 <= score <= 1 + */ + System.out.print(String.format("%7.3f", scores[i][j] / totscore)); } - - System.out.println("\n"); + System.out.println(); } + + System.out.println("\n"); } /** @@ -137,13 +176,14 @@ public class PairwiseAlignPanel extends GPairwiseAlignPanel * @param e * DOCUMENT ME! */ + @Override protected void viewInEditorButton_actionPerformed(ActionEvent e) { - Sequence[] seq = new Sequence[sequences.size()]; + SequenceI[] seq = new SequenceI[sequences.size()]; for (int i = 0; i < sequences.size(); i++) { - seq[i] = (Sequence) sequences.elementAt(i); + seq[i] = sequences.elementAt(i); } AlignFrame af = new AlignFrame(new Alignment(seq), diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 846ba64..850a09a 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -1743,7 +1743,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener } sequence.setName(dialog.getName().replace(' ', '_')); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } sequence.setDescription(dialog.getDescription()); diff --git a/src/jalview/gui/ProgressBar.java b/src/jalview/gui/ProgressBar.java index ea341e3..011d810 100644 --- a/src/jalview/gui/ProgressBar.java +++ b/src/jalview/gui/ProgressBar.java @@ -89,8 +89,8 @@ public class ProgressBar implements IProgressIndicator } this.statusPanel = container; this.statusBar = statusBar; - this.progressBars = new Hashtable(); - this.progressBarHandlers = new Hashtable(); + this.progressBars = new Hashtable<>(); + this.progressBarHandlers = new Hashtable<>(); } @@ -119,46 +119,52 @@ public class ProgressBar implements IProgressIndicator * execution. */ @Override - public void setProgressBar(String message, long id) + public void setProgressBar(final String message, final long id) { - Long longId = Long.valueOf(id); - - JPanel progressPanel = progressBars.get(longId); - if (progressPanel != null) + SwingUtilities.invokeLater(new Runnable() { - /* - * Progress bar is displayed for this id - remove it now, and any handler - */ - progressBars.remove(id); - if (message != null && statusBar != null) - { - statusBar.setText(message); - } - if (progressBarHandlers.containsKey(longId)) + @Override + public void run() { - progressBarHandlers.remove(longId); - } - removeRow(progressPanel); - } - else - { - /* - * No progress bar for this id - add one now - */ - progressPanel = new JPanel(new BorderLayout(10, 5)); + JPanel progressPanel = progressBars.get(id); + if (progressPanel != null) + { + /* + * Progress bar is displayed for this id - remove it now, and any handler + */ + progressBars.remove(id); + if (message != null && statusBar != null) + { + statusBar.setText(message); + } + if (progressBarHandlers.containsKey(id)) + { + progressBarHandlers.remove(id); + } + removeRow(progressPanel); + } + else + { + /* + * No progress bar for this id - add one now + */ + progressPanel = new JPanel(new BorderLayout(10, 5)); - JProgressBar progressBar = new JProgressBar(); - progressBar.setIndeterminate(true); + JProgressBar progressBar = new JProgressBar(); + progressBar.setIndeterminate(true); - progressPanel.add(new JLabel(message), BorderLayout.WEST); - progressPanel.add(progressBar, BorderLayout.CENTER); + progressPanel.add(new JLabel(message), BorderLayout.WEST); + progressPanel.add(progressBar, BorderLayout.CENTER); - addRow(progressPanel); + addRow(progressPanel); - progressBars.put(longId, progressPanel); - } + progressBars.put(id, progressPanel); + } + + refreshLayout(); + } + }); - refreshLayout(); } /** @@ -215,41 +221,50 @@ public class ProgressBar implements IProgressIndicator public void registerHandler(final long id, final IProgressIndicatorHandler handler) { - Long longId = Long.valueOf(id); - final JPanel progressPanel = progressBars.get(longId); - if (progressPanel == null) - { - System.err.println( - "call setProgressBar before registering the progress bar's handler."); - return; - } - - /* - * Nothing useful to do if not a Cancel handler - */ - if (!handler.canCancel()) - { - return; - } - - progressBarHandlers.put(longId, handler); - JButton cancel = new JButton(MessageManager.getString("action.cancel")); final IProgressIndicator us = this; - cancel.addActionListener(new ActionListener() - { + SwingUtilities.invokeLater(new Runnable() + { @Override - public void actionPerformed(ActionEvent e) + public void run() { - handler.cancelActivity(id); - us.setProgressBar(MessageManager - .formatMessage("label.cancelled_params", new Object[] - { ((JLabel) progressPanel.getComponent(0)).getText() }), - id); + final JPanel progressPanel = progressBars.get(id); + if (progressPanel == null) + { + System.err.println( + "call setProgressBar before registering the progress bar's handler."); + return; + } + + /* + * Nothing useful to do if not a Cancel handler + */ + if (!handler.canCancel()) + { + return; + } + + progressBarHandlers.put(id, handler); + JButton cancel = new JButton( + MessageManager.getString("action.cancel")); + cancel.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + handler.cancelActivity(id); + us.setProgressBar(MessageManager + .formatMessage("label.cancelled_params", new Object[] + { ((JLabel) progressPanel.getComponent(0)).getText() }), + id); + } + }); + progressPanel.add(cancel, BorderLayout.EAST); + refreshLayout(); + } }); - progressPanel.add(cancel, BorderLayout.EAST); - refreshLayout(); } } diff --git a/src/jalview/gui/PromptUserConfig.java b/src/jalview/gui/PromptUserConfig.java index 6261015..cb59452 100644 --- a/src/jalview/gui/PromptUserConfig.java +++ b/src/jalview/gui/PromptUserConfig.java @@ -24,8 +24,6 @@ import jalview.bin.Cache; import java.awt.Component; -import javax.swing.JOptionPane; - public class PromptUserConfig implements Runnable { /** @@ -120,6 +118,7 @@ public class PromptUserConfig implements Runnable this.allowCancel = allowCancel; } + @Override public void run() { if (property == null) @@ -206,12 +205,7 @@ public class PromptUserConfig implements Runnable (allowCancel) ? JvOptionPane.YES_NO_CANCEL_OPTION : JvOptionPane.YES_NO_OPTION, JvOptionPane.QUESTION_MESSAGE); - // now, ask the desktop to relayer any external windows that might have - // been obsured - if (Desktop.instance != null) - { - Desktop.instance.relayerWindows(); - } + // and finish parsing the result jalview.bin.Cache.log.debug("Got response : " + reply); if (reply == JvOptionPane.YES_OPTION) diff --git a/src/jalview/gui/RedundancyPanel.java b/src/jalview/gui/RedundancyPanel.java index 8bf2fba..c4390c0 100755 --- a/src/jalview/gui/RedundancyPanel.java +++ b/src/jalview/gui/RedundancyPanel.java @@ -54,7 +54,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable AlignmentPanel ap; - Stack historyList = new Stack(); + Stack historyList = new Stack<>(); // simpler than synching with alignFrame. @@ -194,7 +194,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable } float value = slider.getValue(); - List redundantSequences = new ArrayList(); + List redundantSequences = new ArrayList<>(); for (int i = 0; i < redundancy.length; i++) { if (value <= redundancy[i]) @@ -295,7 +295,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable af.updateEditMenuBar(); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); if (historyList.size() == 0) { diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index 1db4051..e677769 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -168,7 +168,7 @@ public class ScalePanel extends JPanel { av.showColumn(reveal[0]); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -184,7 +184,7 @@ public class ScalePanel extends JPanel { av.showAllHiddenColumns(); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -208,7 +208,7 @@ public class ScalePanel extends JPanel av.setSelectionGroup(null); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -260,7 +260,7 @@ public class ScalePanel extends JPanel sg.setEndRes(max); } av.setSelectionGroup(sg); - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -297,7 +297,7 @@ public class ScalePanel extends JPanel } else { - ap.paintAlignment(false); + ap.paintAlignment(false, false); } return; } @@ -316,7 +316,7 @@ public class ScalePanel extends JPanel } } stretchingGroup = false; - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -346,7 +346,7 @@ public class ScalePanel extends JPanel { stretchingGroup = true; cs.stretchGroup(res, sg, min, max); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 4e896a0..2a9c704 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -27,6 +27,7 @@ import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.renderer.ScaleRenderer; import jalview.renderer.ScaleRenderer.ScaleMark; +import jalview.util.Comparison; import jalview.viewmodel.ViewportListenerI; import jalview.viewmodel.ViewportRanges; @@ -46,53 +47,56 @@ import java.util.List; import javax.swing.JComponent; /** - * DOCUMENT ME! + * The Swing component on which the alignment sequences, and annotations (if + * shown), are drawn. This includes scales above, left and right (if shown) in + * Wrapped mode, but not the scale above in Unwrapped mode. * - * @author $author$ - * @version $Revision$ */ public class SeqCanvas extends JComponent implements ViewportListenerI { - private static String ZEROS = "0000000000"; + private static final String ZEROS = "0000000000"; final FeatureRenderer fr; - final SequenceRenderer seqRdr; - BufferedImage img; - Graphics2D gg; - AlignViewport av; - boolean fastPaint = false; + int cursorX = 0; - int labelWidthWest; + int cursorY = 0; - int labelWidthEast; + private final SequenceRenderer seqRdr; - int cursorX = 0; + private boolean fastPaint = false; - int cursorY = 0; + private boolean fastpainting = false; - int charHeight = 0; + private AnnotationPanel annotations; - int charWidth = 0; + /* + * measurements for drawing a wrapped alignment + */ + private int labelWidthEast; // label right width in pixels if shown + + private int labelWidthWest; // label left width in pixels if shown - boolean fastpainting = false; + private int wrappedSpaceAboveAlignment; // gap between widths - AnnotationPanel annotations; + private int wrappedRepeatHeightPx; // height in pixels of wrapped width + + private int wrappedVisibleWidths; // number of wrapped widths displayed + + private Graphics2D gg; /** * Creates a new SeqCanvas object. * - * @param av - * DOCUMENT ME! + * @param ap */ public SeqCanvas(AlignmentPanel ap) { this.av = ap.av; - updateViewport(); fr = new FeatureRenderer(ap); seqRdr = new SequenceRenderer(av); setLayout(new BorderLayout()); @@ -112,29 +116,36 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return fr; } - private void updateViewport() - { - charHeight = av.getCharHeight(); - charWidth = av.getCharWidth(); - } - /** - * DOCUMENT ME! + * Draws the scale above a region of a wrapped alignment, consisting of a + * column number every major interval (10 columns). * * @param g - * DOCUMENT ME! + * the graphics context to draw on, positioned at the start (bottom + * left) of the line on which to draw any scale marks * @param startx - * DOCUMENT ME! + * start alignment column (0..) * @param endx - * DOCUMENT ME! + * end alignment column (0..) * @param ypos - * DOCUMENT ME! + * y offset to draw at */ private void drawNorthScale(Graphics g, int startx, int endx, int ypos) { - updateViewport(); - for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx, - endx)) + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + + /* + * white fill the scale space (for the fastPaint case) + */ + g.setColor(Color.white); + g.fillRect(0, ypos - charHeight - charHeight / 2, getWidth(), + charHeight * 3 / 2 + 2); + g.setColor(Color.black); + + List marks = new ScaleRenderer().calculateMarks(av, startx, + endx); + for (ScaleMark mark : marks) { int mpos = mark.column; // (i - startx - 1) if (mpos < 0) @@ -149,137 +160,119 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2)); } - g.drawLine((mpos * charWidth) + (charWidth / 2), - (ypos + 2) - (charHeight / 2), - (mpos * charWidth) + (charWidth / 2), ypos - 2); + + /* + * draw a tick mark below the column number, centred on the column; + * height of tick mark is 4 pixels less than half a character + */ + int xpos = (mpos * charWidth) + (charWidth / 2); + g.drawLine(xpos, (ypos + 2) - (charHeight / 2), xpos, ypos - 2); } } } /** - * DOCUMENT ME! + * Draw the scale to the left or right of a wrapped alignment * * @param g - * DOCUMENT ME! + * graphics context, positioned at the start of the scale to be drawn * @param startx - * DOCUMENT ME! + * first column of wrapped width (0.. excluding any hidden columns) * @param endx - * DOCUMENT ME! + * last column of wrapped width (0.. excluding any hidden columns) * @param ypos - * DOCUMENT ME! + * vertical offset at which to begin the scale + * @param left + * if true, scale is left of residues, if false, scale is right */ - void drawWestScale(Graphics g, int startx, int endx, int ypos) + void drawVerticalScale(Graphics g, final int startx, final int endx, + final int ypos, final boolean left) { - FontMetrics fm = getFontMetrics(av.getFont()); - ypos += charHeight; + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); - if (av.hasHiddenColumns()) - { - startx = av.getAlignment().getHiddenColumns() - .adjustForHiddenColumns(startx); - endx = av.getAlignment().getHiddenColumns() - .adjustForHiddenColumns(endx); - } + int yPos = ypos + charHeight; + int startX = startx; + int endX = endx; - int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns(); + startX = hiddenColumns.adjustForHiddenColumns(startx); + endX = hiddenColumns.adjustForHiddenColumns(endx); } + FontMetrics fm = getFontMetrics(av.getFont()); - // WEST SCALE for (int i = 0; i < av.getAlignment().getHeight(); i++) { SequenceI seq = av.getAlignment().getSequenceAt(i); - int index = startx; - int value = -1; - while (index < endx) + /* + * find sequence position of first non-gapped position - + * to the right if scale left, to the left if scale right + */ + int index = left ? startX : endX; + int value = -1; + while (index >= startX && index <= endX) { - if (jalview.util.Comparison.isGap(seq.getCharAt(index))) + if (!Comparison.isGap(seq.getCharAt(index))) + { + value = seq.findPosition(index); + break; + } + if (left) { index++; - - continue; } - - value = av.getAlignment().getSequenceAt(i).findPosition(index); - - break; - } - - if (value != -1) - { - int x = labelWidthWest - fm.stringWidth(String.valueOf(value)) - - charWidth / 2; - g.drawString(value + "", x, - (ypos + (i * charHeight)) - (charHeight / 5)); - } - } - } - - /** - * DOCUMENT ME! - * - * @param g - * DOCUMENT ME! - * @param startx - * DOCUMENT ME! - * @param endx - * DOCUMENT ME! - * @param ypos - * DOCUMENT ME! - */ - void drawEastScale(Graphics g, int startx, int endx, int ypos) - { - ypos += charHeight; - - if (av.hasHiddenColumns()) - { - endx = av.getAlignment().getHiddenColumns() - .adjustForHiddenColumns(endx); - } - - SequenceI seq; - // EAST SCALE - for (int i = 0; i < av.getAlignment().getHeight(); i++) - { - seq = av.getAlignment().getSequenceAt(i); - int index = endx; - int value = -1; - - while (index > startx) - { - if (jalview.util.Comparison.isGap(seq.getCharAt(index))) + else { index--; - - continue; } - - value = seq.findPosition(index); - - break; } + /* + * white fill the space for the scale + */ + g.setColor(Color.white); + int y = (yPos + (i * charHeight)) - (charHeight / 5); + // fillRect origin is top left of rectangle + g.fillRect(0, y - charHeight, left ? labelWidthWest : labelWidthEast, + charHeight + 1); + if (value != -1) { - g.drawString(String.valueOf(value), 0, - (ypos + (i * charHeight)) - (charHeight / 5)); + /* + * draw scale value, right justified within its width less half a + * character width padding on the right + */ + int labelSpace = left ? labelWidthWest : labelWidthEast; + labelSpace -= charWidth / 2; // leave space to the right + String valueAsString = String.valueOf(value); + int labelLength = fm.stringWidth(valueAsString); + int xOffset = labelSpace - labelLength; + g.setColor(Color.black); + g.drawString(valueAsString, xOffset, y); } } } - /** - * need to make this thread safe move alignment rendering in response to - * slider adjustment + * Does a fast paint of an alignment in response to a scroll. Most of the + * visible region is simply copied and shifted, and then any newly visible + * columns or rows are drawn. The scroll may be horizontal or vertical, but + * not both at once. Scrolling may be the result of + *

      + *
    • dragging a scroll bar
    • + *
    • clicking in the scroll bar
    • + *
    • scrolling by trackpad, middle mouse button, or other device
    • + *
    • by moving the box in the Overview window
    • + *
    • programmatically to make a highlighted position visible
    • + *
    * * @param horizontal - * shift along + * columns to shift right (positive) or left (negative) * @param vertical - * shift up or down in repaint + * rows to shift down (positive) or up (negative) */ public void fastPaint(int horizontal, int vertical) { @@ -289,15 +282,19 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } fastpainting = true; fastPaint = true; - updateViewport(); - ViewportRanges ranges = av.getRanges(); - int startRes = ranges.getStartRes(); - int endRes = ranges.getEndRes(); - int startSeq = ranges.getStartSeq(); - int endSeq = ranges.getEndSeq(); - int transX = 0; - int transY = 0; + try + { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + + ViewportRanges ranges = av.getRanges(); + int startRes = ranges.getStartRes(); + int endRes = ranges.getEndRes(); + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + int transX = 0; + int transY = 0; gg.copyArea(horizontal * charWidth, vertical * charHeight, img.getWidth(), img.getHeight(), -horizontal * charWidth, @@ -340,15 +337,19 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.translate(-transX, -transY); repaint(); - fastpainting = false; + } finally + { + fastpainting = false; + } } @Override public void paintComponent(Graphics g) { - super.paintComponent(g); - - updateViewport(); + super.paintComponent(g); + + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); ViewportRanges ranges = av.getRanges(); @@ -411,7 +412,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.drawImage(lcimg, 0, 0, this); } } - + /** * Draw an alignment panel for printing * @@ -519,6 +520,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { BufferedImage lcimg = null; + int charWidth = av.getCharWidth(); + int charHeight = av.getCharHeight(); + int width = getWidth(); int height = getHeight(); @@ -558,29 +562,30 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ public int getWrappedCanvasWidth(int canvasWidth) { - FontMetrics fm = getFontMetrics(av.getFont()); + int charWidth = av.getCharWidth(); - labelWidthEast = 0; - labelWidthWest = 0; + FontMetrics fm = getFontMetrics(av.getFont()); - if (av.getScaleRightWrapped()) + int labelWidth = 0; + + if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) { - labelWidthEast = getLabelWidth(fm); + labelWidth = getLabelWidth(fm); } - if (av.getScaleLeftWrapped()) - { - labelWidthWest = labelWidthEast > 0 ? labelWidthEast - : getLabelWidth(fm); - } + labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; + + labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; } /** - * Returns a pixel width suitable for showing the largest sequence coordinate - * (end position) in the alignment. Returns 2 plus the number of decimal - * digits to be shown (3 for 1-10, 4 for 11-99 etc). + * Returns a pixel width sufficient to show the largest sequence coordinate + * (end position) in the alignment, calculated as the FontMetrics width of + * zeroes "0000000" limited to the number of decimal digits to be shown (3 for + * 1-10, 4 for 11-99 etc). One character width is added to this, to allow for + * half a character width space on either side. * * @param fm * @return @@ -598,160 +603,305 @@ public class SeqCanvas extends JComponent implements ViewportListenerI maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd()); } - int length = 2; + int length = 0; for (int i = maxWidth; i > 0; i /= 10) { length++; } - return fm.stringWidth(ZEROS.substring(0, length)); + return fm.stringWidth(ZEROS.substring(0, length)) + av.getCharWidth(); } /** - * DOCUMENT ME! + * Draws as many widths of a wrapped alignment as can fit in the visible + * window * * @param g - * DOCUMENT ME! * @param canvasWidth - * DOCUMENT ME! + * available width in pixels * @param canvasHeight - * DOCUMENT ME! - * @param startRes - * DOCUMENT ME! + * available height in pixels + * @param startColumn + * the first column (0...) of the alignment to draw */ - private void drawWrappedPanel(Graphics g, int canvasWidth, - int canvasHeight, int startRes) + public void drawWrappedPanel(Graphics g, int canvasWidth, + int canvasHeight, final int startColumn) { - updateViewport(); - AlignmentI al = av.getAlignment(); + int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth, + canvasHeight); - int labelWidth = 0; - if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) + av.setWrappedWidth(wrappedWidthInResidues); + + ViewportRanges ranges = av.getRanges(); + ranges.setViewportStartAndWidth(startColumn, wrappedWidthInResidues); + + /* + * draw one width at a time (including any scales or annotation shown), + * until we have run out of either alignment or vertical space available + */ + int ypos = wrappedSpaceAboveAlignment; + int maxWidth = ranges.getVisibleAlignmentWidth(); + + int start = startColumn; + int currentWidth = 0; + while ((currentWidth < wrappedVisibleWidths) && (start < maxWidth)) { - FontMetrics fm = getFontMetrics(av.getFont()); - labelWidth = getLabelWidth(fm); + int endColumn = Math + .min(maxWidth, start + wrappedWidthInResidues - 1); + drawWrappedWidth(g, ypos, start, endColumn, canvasHeight); + ypos += wrappedRepeatHeightPx; + start += wrappedWidthInResidues; + currentWidth++; } - labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; - labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; + drawWrappedDecorators(g, startColumn); + } - int hgap = charHeight; - if (av.getScaleAboveWrapped()) + /** + * Calculates and saves values needed when rendering a wrapped alignment. + * These depend on many factors, including + *
      + *
    • canvas width and height
    • + *
    • number of visible sequences, and height of annotations if shown
    • + *
    • font and character width
    • + *
    • whether scales are shown left, right or above the alignment
    • + *
    + * + * @param canvasWidth + * @param canvasHeight + * @return the number of residue columns in each width + */ + protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight) + { + int charHeight = av.getCharHeight(); + + /* + * vertical space in pixels between wrapped widths of alignment + * - one character height, or two if scale above is drawn + */ + wrappedSpaceAboveAlignment = charHeight + * (av.getScaleAboveWrapped() ? 2 : 1); + + /* + * height in pixels of the wrapped widths + */ + wrappedRepeatHeightPx = wrappedSpaceAboveAlignment; + // add sequences + wrappedRepeatHeightPx += av.getRanges().getViewportHeight() + * charHeight; + // add annotations panel height if shown + wrappedRepeatHeightPx += getAnnotationHeight(); + + /* + * number of visible widths (the last one may be part height), + * ensuring a part height includes at least one sequence + */ + ViewportRanges ranges = av.getRanges(); + wrappedVisibleWidths = canvasHeight / wrappedRepeatHeightPx; + int remainder = canvasHeight % wrappedRepeatHeightPx; + if (remainder >= (wrappedSpaceAboveAlignment + charHeight)) { - hgap += charHeight; + wrappedVisibleWidths++; } - int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; - int cHeight = av.getAlignment().getHeight() * charHeight; + /* + * compute width in residues; this also sets East and West label widths + */ + int wrappedWidthInResidues = getWrappedCanvasWidth(canvasWidth); - av.setWrappedWidth(cWidth); + /* + * limit visibleWidths to not exceed width of alignment + */ + int xMax = ranges.getVisibleAlignmentWidth(); + int startToEnd = xMax - ranges.getStartRes(); + int maxWidths = startToEnd / wrappedWidthInResidues; + if (startToEnd % wrappedWidthInResidues > 0) + { + maxWidths++; + } + wrappedVisibleWidths = Math.min(wrappedVisibleWidths, maxWidths); - av.getRanges().setViewportStartAndWidth(startRes, cWidth); + return wrappedWidthInResidues; + } - int endx; - int ypos = hgap; - int maxwidth = av.getAlignment().getWidth(); + /** + * Draws one width of a wrapped alignment, including sequences and + * annnotations, if shown, but not scales or hidden column markers + * + * @param g + * @param ypos + * @param startColumn + * @param endColumn + * @param canvasHeight + */ + protected void drawWrappedWidth(Graphics g, int ypos, int startColumn, + int endColumn, int canvasHeight) + { + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); - if (av.hasHiddenColumns()) + int endx = Math.min(startColumn + viewportWidth - 1, endColumn); + + /* + * move right before drawing by the width of the scale left (if any) + * plus column offset from left margin (usually zero, but may be non-zero + * when fast painting is drawing just a few columns) + */ + int charWidth = av.getCharWidth(); + int xOffset = labelWidthWest + + ((startColumn - ranges.getStartRes()) % viewportWidth) + * charWidth; + g.translate(xOffset, 0); + + // When printing we have an extra clipped region, + // the Printable page which we need to account for here + Shape clip = g.getClip(); + + if (clip == null) { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth); + g.setClip(0, 0, viewportWidth * charWidth, canvasHeight); + } + else + { + g.setClip(0, (int) clip.getBounds().getY(), + viewportWidth * charWidth, (int) clip.getBounds().getHeight()); } - int annotationHeight = getAnnotationHeight(); + /* + * white fill the region to be drawn (so incremental fast paint doesn't + * scribble over an existing image) + */ + gg.setColor(Color.white); + gg.fillRect(0, ypos, (endx - startColumn + 1) * charWidth, + wrappedRepeatHeightPx); - while ((ypos <= canvasHeight) && (startRes < maxwidth)) - { - endx = startRes + cWidth - 1; + drawPanel(g, startColumn, endx, 0, av.getAlignment().getHeight() - 1, + ypos); - if (endx > maxwidth) + int cHeight = av.getAlignment().getHeight() * av.getCharHeight(); + + if (av.isShowAnnotation()) + { + g.translate(0, cHeight + ypos + 3); + if (annotations == null) { - endx = maxwidth; + annotations = new AnnotationPanel(av); } - g.setFont(av.getFont()); - g.setColor(Color.black); + annotations.renderer.drawComponent(annotations, av, g, -1, + startColumn, endx + 1); + g.translate(0, -cHeight - ypos - 3); + } + g.setClip(clip); + g.translate(-xOffset, 0); + } + + /** + * Draws scales left, right and above (if shown), and any hidden column + * markers, on all widths of the wrapped alignment + * + * @param g + * @param startColumn + */ + protected void drawWrappedDecorators(Graphics g, final int startColumn) + { + int charWidth = av.getCharWidth(); + + g.setFont(av.getFont()); + g.setColor(Color.black); + + int ypos = wrappedSpaceAboveAlignment; + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); + int maxWidth = ranges.getVisibleAlignmentWidth(); + int widthsDrawn = 0; + int startCol = startColumn; + + while (widthsDrawn < wrappedVisibleWidths) + { + int endColumn = Math.min(maxWidth, startCol + viewportWidth - 1); if (av.getScaleLeftWrapped()) { - drawWestScale(g, startRes, endx, ypos); + drawVerticalScale(g, startCol, endColumn - 1, ypos, true); } if (av.getScaleRightWrapped()) { - g.translate(canvasWidth - labelWidthEast, 0); - drawEastScale(g, startRes, endx, ypos); - g.translate(-(canvasWidth - labelWidthEast), 0); + int x = labelWidthWest + viewportWidth * charWidth; + g.translate(x, 0); + drawVerticalScale(g, startCol, endColumn, ypos, false); + g.translate(-x, 0); } + /* + * white fill region of scale above and hidden column markers + * (to support incremental fast paint of image) + */ + g.translate(labelWidthWest, 0); + g.setColor(Color.white); + g.fillRect(0, ypos - wrappedSpaceAboveAlignment, viewportWidth + * charWidth + labelWidthWest, wrappedSpaceAboveAlignment); + g.setColor(Color.black); + g.translate(-labelWidthWest, 0); + g.translate(labelWidthWest, 0); if (av.getScaleAboveWrapped()) { - drawNorthScale(g, startRes, endx, ypos); + drawNorthScale(g, startCol, endColumn, ypos); } if (av.hasHiddenColumns() && av.getShowHiddenMarkers()) { - g.setColor(Color.blue); - int res; - HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - List positions = hidden.findHiddenRegionPositions(); - for (int pos : positions) - { - res = pos - startRes; - - if (res < 0 || res > endx - startRes) - { - continue; - } - - gg.fillPolygon( - new int[] - { res * charWidth - charHeight / 4, - res * charWidth + charHeight / 4, res * charWidth }, - new int[] - { ypos - (charHeight / 2), ypos - (charHeight / 2), - ypos - (charHeight / 2) + 8 }, - 3); - - } + drawHiddenColumnMarkers(g, ypos, startCol, endColumn); } - // When printing we have an extra clipped region, - // the Printable page which we need to account for here - Shape clip = g.getClip(); + g.translate(-labelWidthWest, 0); - if (clip == null) - { - g.setClip(0, 0, cWidth * charWidth, canvasHeight); - } - else - { - g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth, - (int) clip.getBounds().getHeight()); - } + ypos += wrappedRepeatHeightPx; + startCol += viewportWidth; + widthsDrawn++; + } + } + + /** + * Draws markers (triangles) above hidden column positions between startColumn + * and endColumn. + * + * @param g + * @param ypos + * @param startColumn + * @param endColumn + */ + protected void drawHiddenColumnMarkers(Graphics g, int ypos, + int startColumn, int endColumn) + { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); - drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos); + g.setColor(Color.blue); + HiddenColumns hidden = av.getAlignment().getHiddenColumns(); + List positions = hidden.findHiddenRegionPositions(); + for (int pos : positions) + { + int res = pos - startColumn; - if (av.isShowAnnotation()) + if (res < 0 || res > endColumn - startColumn + 1) { - g.translate(0, cHeight + ypos + 3); - if (annotations == null) - { - annotations = new AnnotationPanel(av); - } - - annotations.renderer.drawComponent(annotations, av, g, -1, startRes, - endx + 1); - g.translate(0, -cHeight - ypos - 3); + continue; } - g.setClip(clip); - g.translate(-labelWidthWest, 0); - - ypos += cHeight + annotationHeight + hgap; - startRes += cWidth; + /* + * draw a downward-pointing triangle at the hidden columns location + * (before the following visible column) + */ + int xMiddle = res * charWidth; + int[] xPoints = new int[] { xMiddle - charHeight / 4, + xMiddle + charHeight / 4, xMiddle }; + int yTop = ypos - (charHeight / 2); + int[] yPoints = new int[] { yTop, yTop, yTop + 8 }; + g.fillPolygon(xPoints, yPoints, 3); } } @@ -762,6 +912,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int canvasWidth, int canvasHeight, int startRes) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + // height gap above each panel int hgap = charHeight; if (av.getScaleAboveWrapped()) @@ -834,22 +987,24 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * marker. * * @param g1 - * Graphics object to draw with + * the graphics context, positioned at the first residue to be drawn * @param startRes - * offset of the first column in the visible region (0..) + * offset of the first column to draw (0..) * @param endRes - * offset of the last column in the visible region (0..) + * offset of the last column to draw (0..) * @param startSeq - * offset of the first sequence in the visible region (0..) + * offset of the first sequence to draw (0..) * @param endSeq - * offset of the last sequence in the visible region (0..) + * offset of the last sequence to draw (0..) * @param yOffset * vertical offset at which to draw (for wrapped alignments) */ public void drawPanel(Graphics g1, final int startRes, final int endRes, final int startSeq, final int endSeq, final int yOffset) { - updateViewport(); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + if (!av.hasHiddenColumns()) { draw(g1, startRes, endRes, startSeq, endSeq, yOffset); @@ -939,6 +1094,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void draw(Graphics g, int startRes, int endRes, int startSeq, int endSeq, int offset) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + g.setFont(av.getFont()); seqRdr.prepare(g, av.isRenderGaps()); @@ -1118,6 +1276,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, int offset) { + int charWidth = av.getCharWidth(); + if (!av.hasHiddenColumns()) { drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, @@ -1179,6 +1339,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int startRes, int endRes, int startSeq, int endSeq, int verticalOffset) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + int visWidth = (endRes - startRes + 1) * charWidth; int oldY = -1; @@ -1347,14 +1510,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return false; } boolean wrapped = av.getWrapAlignment(); - try { fastPaint = !noFastPaint; fastpainting = fastPaint; - updateViewport(); - /* * to avoid redrawing the whole visible region, we instead * redraw just the minimal regions to remove previous highlights @@ -1498,52 +1658,303 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { fastPaint = true; repaint(); + return; } - else if (av.getWrapAlignment()) + + int scrollX = 0; + if (eventName.equals(ViewportRanges.STARTRES)) { - if (eventName.equals(ViewportRanges.STARTRES)) + // Make sure we're not trying to draw a panel + // larger than the visible window + ViewportRanges vpRanges = av.getRanges(); + scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); + int range = vpRanges.getViewportWidth(); + if (scrollX > range) { - repaint(); + scrollX = range; + } + else if (scrollX < -range) + { + scrollX = -range; } } - else + + // 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. + + // scroll - startres and endres both change + if (eventName.equals(ViewportRanges.STARTRES)) { - int scrollX = 0; - if (eventName.equals(ViewportRanges.STARTRES)) + if (av.getWrapAlignment()) { - // Make sure we're not trying to draw a panel - // larger than the visible window - ViewportRanges vpRanges = av.getRanges(); - scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); - int range = vpRanges.getEndRes() - vpRanges.getStartRes(); - if (scrollX > range) - { - scrollX = range; - } - else if (scrollX < -range) - { - scrollX = -range; - } + fastPaintWrapped(scrollX); } - - // 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)) + else { - // scroll - startres and endres both change fastPaint(scrollX, 0); } - else if (eventName.equals(ViewportRanges.STARTSEQ)) + } + else if (eventName.equals(ViewportRanges.STARTSEQ)) + { + // scroll + fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); + } + } + + /** + * Does a minimal update of the image for a scroll movement. This method + * handles scroll movements of up to one width of the wrapped alignment (one + * click in the vertical scrollbar). Larger movements (for example after a + * scroll to highlight a mapped position) trigger a full redraw instead. + * + * @param scrollX + * number of positions scrolled (right if positive, left if negative) + */ + protected void fastPaintWrapped(int scrollX) + { + ViewportRanges ranges = av.getRanges(); + + if (Math.abs(scrollX) > ranges.getViewportWidth()) + { + /* + * shift of more than one view width is + * overcomplicated to handle in this method + */ + fastPaint = false; + repaint(); + return; + } + + if (fastpainting || gg == null) + { + return; + } + + fastPaint = true; + fastpainting = true; + + try + { + calculateWrappedGeometry(getWidth(), getHeight()); + + /* + * relocate the regions of the alignment that are still visible + */ + shiftWrappedAlignment(-scrollX); + + /* + * add new columns (sequence, annotation) + * - at top left if scrollX < 0 + * - at right of last two widths if scrollX > 0 + */ + if (scrollX < 0) + { + int startRes = ranges.getStartRes(); + drawWrappedWidth(gg, wrappedSpaceAboveAlignment, startRes, startRes + - scrollX - 1, getHeight()); + } + else + { + fastPaintWrappedAddRight(scrollX); + } + + /* + * draw all scales (if shown) and hidden column markers + */ + drawWrappedDecorators(gg, ranges.getStartRes()); + + repaint(); + } finally + { + fastpainting = false; + } + } + + /** + * Draws the specified number of columns at the 'end' (bottom right) of a + * wrapped alignment view, including sequences and annotations if shown, but + * not scales. Also draws the same number of columns at the right hand end of + * the second last width shown, if the last width is not full height (so + * cannot simply be copied from the graphics image). + * + * @param columns + */ + protected void fastPaintWrappedAddRight(int columns) + { + if (columns == 0) + { + return; + } + + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); + int charWidth = av.getCharWidth(); + + /** + * draw full height alignment in the second last row, last columns, if the + * last row was not full height + */ + int visibleWidths = wrappedVisibleWidths; + int canvasHeight = getHeight(); + boolean lastWidthPartHeight = (wrappedVisibleWidths * wrappedRepeatHeightPx) > canvasHeight; + + if (lastWidthPartHeight) + { + int widthsAbove = Math.max(0, visibleWidths - 2); + int ypos = wrappedRepeatHeightPx * widthsAbove + + wrappedSpaceAboveAlignment; + int endRes = ranges.getEndRes(); + endRes += widthsAbove * viewportWidth; + int startRes = endRes - columns; + int xOffset = ((startRes - ranges.getStartRes()) % viewportWidth) + * charWidth; + + /* + * white fill first to erase annotations + */ + gg.translate(xOffset, 0); + gg.setColor(Color.white); + gg.fillRect(labelWidthWest, ypos, + (endRes - startRes + 1) * charWidth, wrappedRepeatHeightPx); + gg.translate(-xOffset, 0); + + drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + } + + /* + * draw newly visible columns in last wrapped width (none if we + * have reached the end of the alignment) + * y-offset for drawing last width is height of widths above, + * plus one gap row + */ + int widthsAbove = visibleWidths - 1; + int ypos = wrappedRepeatHeightPx * widthsAbove + + wrappedSpaceAboveAlignment; + int endRes = ranges.getEndRes(); + endRes += widthsAbove * viewportWidth; + int startRes = endRes - columns + 1; + + /* + * white fill first to erase annotations + */ + int xOffset = ((startRes - ranges.getStartRes()) % viewportWidth) + * charWidth; + gg.translate(xOffset, 0); + gg.setColor(Color.white); + int width = viewportWidth * charWidth - xOffset; + gg.fillRect(labelWidthWest, ypos, width, wrappedRepeatHeightPx); + gg.translate(-xOffset, 0); + + gg.setFont(av.getFont()); + gg.setColor(Color.black); + + if (startRes < ranges.getVisibleAlignmentWidth()) + { + drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + } + + /* + * and finally, white fill any space below the visible alignment + */ + int heightBelow = canvasHeight - visibleWidths * wrappedRepeatHeightPx; + if (heightBelow > 0) + { + gg.setColor(Color.white); + gg.fillRect(0, canvasHeight - heightBelow, getWidth(), heightBelow); + } + } + + /** + * Shifts the visible alignment by the specified number of columns - left if + * negative, right if positive. Copies and moves sequences and annotations (if + * shown). Scales, hidden column markers and any newly visible columns must be + * drawn separately. + * + * @param positions + */ + protected void shiftWrappedAlignment(int positions) + { + if (positions == 0) + { + return; + } + int charWidth = av.getCharWidth(); + + int canvasHeight = getHeight(); + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); + int widthToCopy = (ranges.getViewportWidth() - Math.abs(positions)) + * charWidth; + int heightToCopy = wrappedRepeatHeightPx - wrappedSpaceAboveAlignment; + int xMax = ranges.getVisibleAlignmentWidth(); + + if (positions > 0) + { + /* + * shift right (after scroll left) + * for each wrapped width (starting with the last), copy (width-positions) + * columns from the left margin to the right margin, and copy positions + * columns from the right margin of the row above (if any) to the + * left margin of the current row + */ + + /* + * get y-offset of last wrapped width, first row of sequences + */ + int y = canvasHeight / wrappedRepeatHeightPx * wrappedRepeatHeightPx; + y += wrappedSpaceAboveAlignment; + int copyFromLeftStart = labelWidthWest; + int copyFromRightStart = copyFromLeftStart + widthToCopy; + + while (y >= 0) + { + gg.copyArea(copyFromLeftStart, y, widthToCopy, heightToCopy, + positions * charWidth, 0); + if (y > 0) + { + gg.copyArea(copyFromRightStart, y - wrappedRepeatHeightPx, + positions * charWidth, heightToCopy, -widthToCopy, + wrappedRepeatHeightPx); + } + + y -= wrappedRepeatHeightPx; + } + } + else + { + /* + * shift left (after scroll right) + * for each wrapped width (starting with the first), copy (width-positions) + * columns from the right margin to the left margin, and copy positions + * columns from the left margin of the row below (if any) to the + * right margin of the current row + */ + int xpos = av.getRanges().getStartRes(); + int y = wrappedSpaceAboveAlignment; + int copyFromRightStart = labelWidthWest - positions * charWidth; + + while (y < canvasHeight) { - // scroll - fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); + gg.copyArea(copyFromRightStart, y, widthToCopy, heightToCopy, + positions * charWidth, 0); + if (y + wrappedRepeatHeightPx < canvasHeight - wrappedRepeatHeightPx + && (xpos + viewportWidth <= xMax)) + { + gg.copyArea(labelWidthWest, y + wrappedRepeatHeightPx, -positions + * charWidth, heightToCopy, widthToCopy, + -wrappedRepeatHeightPx); + } + + y += wrappedRepeatHeightPx; + xpos += viewportWidth; } } } + /** * Redraws any positions in the search results in the visible region of a * wrapped alignment. Any highlights are drawn depending on the search results @@ -1560,11 +1971,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return false; } - + int charHeight = av.getCharHeight(); + boolean matchFound = false; + calculateWrappedGeometry(getWidth(), getHeight()); int wrappedWidth = av.getWrappedWidth(); - int wrappedHeight = getRepeatHeightWrapped(); + int wrappedHeight = wrappedRepeatHeightPx; ViewportRanges ranges = av.getRanges(); int canvasHeight = getHeight(); @@ -1663,23 +2076,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } /** - * Answers the height in pixels of a repeating section of the wrapped - * alignment, including space above, scale above if shown, sequences, and - * annotation panel if shown + * Answers the width in pixels of the left scale labels (0 if not shown) * * @return */ - protected int getRepeatHeightWrapped() + int getLabelWidthWest() { - // gap (and maybe scale) above - int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); - - // add sequences - repeatHeight += av.getRanges().getViewportHeight() * charHeight; - - // add annotations panel height if shown - repeatHeight += getAnnotationHeight(); - - return repeatHeight; + return labelWidthWest; } } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index e99e577..2223ee5 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -215,8 +215,8 @@ public class SeqPanel extends JPanel + hgap + seqCanvas.getAnnotationHeight(); int y = evt.getY(); - y -= hgap; - x = Math.max(0, x - seqCanvas.labelWidthWest); + y = Math.max(0, y - hgap); + x = Math.max(0, x - seqCanvas.getLabelWidthWest()); int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth()); if (cwidth < 1) @@ -484,7 +484,7 @@ public class SeqPanel extends JPanel av.setSelectionGroup(sg); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -719,10 +719,12 @@ public class SeqPanel extends JPanel } /** - * DOCUMENT ME! + * Action on mouse movement is to update the status bar to show the current + * sequence position, and (if features are shown) to show any features at the + * position in a tooltip. Does nothing if the mouse move does not change + * residue position. * * @param evt - * DOCUMENT ME! */ @Override public void mouseMoved(MouseEvent evt) @@ -735,7 +737,8 @@ public class SeqPanel extends JPanel } final int column = findColumn(evt); - int seq = findSeq(evt); + final int seq = findSeq(evt); + if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight()) { lastMouseSeq = -1; @@ -852,11 +855,12 @@ public class SeqPanel extends JPanel /** * set when the current UI interaction has resulted in a change that requires - * overview shading to be recalculated. this could be changed to something - * more expressive that indicates what actually has changed, so selective - * redraws can be applied + * shading in overviews and structures to be recalculated. this could be + * changed to a something more expressive that indicates what actually has + * changed, so selective redraws can be applied (ie. only structures, only + * overview, etc) */ - private boolean needOverviewUpdate = false; // TODO: refactor to avcontroller + private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller /** * set if av.getSelectionGroup() refers to a group that is defined on the @@ -1057,7 +1061,7 @@ public class SeqPanel extends JPanel } if (newWidth > 0) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); if (copyChanges) { /* @@ -1626,7 +1630,7 @@ public class SeqPanel extends JPanel av.getRanges().scrollRight(true); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(false); } @@ -1637,12 +1641,18 @@ public class SeqPanel extends JPanel { av.getRanges().scrollRight(false); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(true); } } - // TODO Update tooltip for new position. + + /* + * update status bar and tooltip for new position + * (need to synthesize a mouse movement to refresh tooltip) + */ + mouseMoved(e); + ToolTipManager.sharedInstance().mouseMoved(e); } /** @@ -1656,7 +1666,7 @@ public class SeqPanel extends JPanel final int res = findColumn(evt); final int seq = findSeq(evt); oldSeq = seq; - needOverviewUpdate = false; + updateOverviewAndStructs = false; startWrapBlock = wrappedBlock; @@ -1821,7 +1831,7 @@ public class SeqPanel extends JPanel // always do this - annotation has own state // but defer colourscheme update until hidden sequences are passed in boolean vischange = stretchGroup.recalcConservation(true); - needOverviewUpdate |= vischange && av.isSelectionDefinedGroup() + updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup() && afterDrag; if (stretchGroup.cs != null) { @@ -1841,8 +1851,10 @@ public class SeqPanel extends JPanel } } PaintRefresher.Refresh(this, av.getSequenceSetId()); - ap.paintAlignment(needOverviewUpdate); - needOverviewUpdate = false; + // TODO: structure colours only need updating if stretchGroup used to or now + // does contain sequences with structure views + ap.paintAlignment(updateOverviewAndStructs, updateOverviewAndStructs); + updateOverviewAndStructs = false; changeEndRes = false; changeStartRes = false; stretchGroup = null; @@ -1896,7 +1908,7 @@ public class SeqPanel extends JPanel if (res > (stretchGroup.getStartRes() - 1)) { stretchGroup.setEndRes(res); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } else if (changeStartRes) @@ -1904,7 +1916,7 @@ public class SeqPanel extends JPanel if (res < (stretchGroup.getEndRes() + 1)) { stretchGroup.setStartRes(res); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } @@ -1938,7 +1950,7 @@ public class SeqPanel extends JPanel if (stretchGroup.getSequences(null).contains(nextSeq)) { stretchGroup.deleteSequence(seq, false); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } else { @@ -1948,7 +1960,7 @@ public class SeqPanel extends JPanel } stretchGroup.addSequence(nextSeq, false); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java index e05230b..8d46792 100755 --- a/src/jalview/gui/SequenceFetcher.java +++ b/src/jalview/gui/SequenceFetcher.java @@ -26,6 +26,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.fts.core.GFTSPanel; import jalview.fts.service.pdb.PDBFTSPanel; import jalview.fts.service.uniprot.UniprotFTSPanel; import jalview.io.FileFormatI; @@ -78,6 +79,8 @@ public class SequenceFetcher extends JPanel implements Runnable JButton close = new JButton(); + JButton back = new JButton(); + JPanel jPanel1 = new JPanel(); JTextArea textArea = new JTextArea(); @@ -383,6 +386,15 @@ public class SequenceFetcher extends JPanel implements Runnable .getString("label.additional_sequence_fetcher")); } + GFTSPanel parentFTSframe = null; + /** + * change the buttons so they fit with the FTS panel. + */ + public void embedWithFTSPanel(GFTSPanel toClose) + { + back.setVisible(true); + parentFTSframe = toClose; + } private void jbInit() throws Exception { this.setLayout(borderLayout2); @@ -427,7 +439,7 @@ public class SequenceFetcher extends JPanel implements Runnable example_actionPerformed(); } }); - close.setText(MessageManager.getString("action.close")); + close.setText(MessageManager.getString("action.cancel")); close.addActionListener(new ActionListener() { @Override @@ -436,6 +448,17 @@ public class SequenceFetcher extends JPanel implements Runnable close_actionPerformed(e); } }); + back.setText(MessageManager.getString("action.back")); + back.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + parentFTSframe.btn_back_ActionPerformed(); + } + }); + // back not visible unless embedded + back.setVisible(false); textArea.setFont(JvSwingUtils.getLabelFont()); textArea.setLineWrap(true); textArea.addKeyListener(new KeyAdapter() @@ -451,9 +474,10 @@ public class SequenceFetcher extends JPanel implements Runnable }); jPanel3.setLayout(borderLayout1); borderLayout1.setVgap(5); - jPanel1.add(ok); + jPanel1.add(back); jPanel1.add(example); jPanel1.add(clear); + jPanel1.add(ok); jPanel1.add(close); jPanel2.setLayout(borderLayout3); databaseButt = /*database.getDatabaseSelectorButton(); @@ -582,6 +606,10 @@ public class SequenceFetcher extends JPanel implements Runnable try { frame.setClosed(true); + if (parentFTSframe!=null) + { + parentFTSframe.btn_cancel_ActionPerformed(); + } } catch (Exception ex) { } @@ -594,7 +622,7 @@ public class SequenceFetcher extends JPanel implements Runnable textArea.setEnabled(false); ok.setEnabled(false); close.setEnabled(false); - + back.setEnabled(false); Thread worker = new Thread(this); worker.start(); } @@ -606,6 +634,7 @@ public class SequenceFetcher extends JPanel implements Runnable textArea.setEnabled(true); ok.setEnabled(true); close.setEnabled(true); + back.setEnabled(parentFTSframe != null); } @Override @@ -1087,4 +1116,9 @@ public class SequenceFetcher extends JPanel implements Runnable { frame.setVisible(false); } + + public void setDatabaseChooserVisible(boolean b) + { + databaseButt.setVisible(b); + } } diff --git a/src/jalview/gui/SliderPanel.java b/src/jalview/gui/SliderPanel.java index e6ec822..93a2457 100755 --- a/src/jalview/gui/SliderPanel.java +++ b/src/jalview/gui/SliderPanel.java @@ -127,7 +127,7 @@ public class SliderPanel extends GSliderPanel @Override public void mouseReleased(MouseEvent evt) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } }); diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index da10e3f..37632ef 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -157,8 +157,8 @@ public class StructureChooser extends GStructureChooser Collection wantedFields = pdbDocFieldPrefs .getStructureSummaryFields(); - discoveredStructuresSet = new LinkedHashSet(); - HashSet errors = new HashSet(); + discoveredStructuresSet = new LinkedHashSet<>(); + HashSet errors = new HashSet<>(); for (SequenceI seq : selectedSequences) { FTSRestRequest pdbRequest = new FTSRestRequest(); @@ -223,7 +223,7 @@ public class StructureChooser extends GStructureChooser public void loadLocalCachedPDBEntries() { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (SequenceI seq : selectedSequences) { if (seq.getDatasetSequence() != null @@ -257,7 +257,7 @@ public class StructureChooser extends GStructureChooser boolean isPDBRefsFound = false; boolean isUniProtRefsFound = false; StringBuilder queryBuilder = new StringBuilder(); - Set seqRefs = new LinkedHashSet(); + Set seqRefs = new LinkedHashSet<>(); if (seq.getAllPDBEntries() != null && queryBuilder.length() < MAX_QLENGTH) @@ -401,8 +401,8 @@ public class StructureChooser extends GStructureChooser lbl_loading.setVisible(true); Collection wantedFields = pdbDocFieldPrefs .getStructureSummaryFields(); - Collection filteredResponse = new HashSet(); - HashSet errors = new HashSet(); + Collection filteredResponse = new HashSet<>(); + HashSet errors = new HashSet<>(); for (SequenceI seq : selectedSequences) { @@ -453,7 +453,7 @@ public class StructureChooser extends GStructureChooser if (!filteredResponse.isEmpty()) { final int filterResponseCount = filteredResponse.size(); - Collection reorderedStructuresSet = new LinkedHashSet(); + Collection reorderedStructuresSet = new LinkedHashSet<>(); reorderedStructuresSet.addAll(filteredResponse); reorderedStructuresSet.addAll(discoveredStructuresSet); getResultTable().setModel(FTSRestResponse @@ -725,11 +725,10 @@ public class StructureChooser extends GStructureChooser @Override public void ok_ActionPerformed() { - final long progressSessionId = System.currentTimeMillis(); final StructureSelectionManager ssm = ap.getStructureSelectionManager(); + final int preferredHeight = pnl_filter.getHeight(); - ssm.setProgressIndicator(this); - ssm.setProgressSessionId(progressSessionId); + new Thread(new Runnable() { @Override @@ -747,7 +746,7 @@ public class StructureChooser extends GStructureChooser int[] selectedRows = getResultTable().getSelectedRows(); PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length]; int count = 0; - List selectedSeqsToView = new ArrayList(); + List selectedSeqsToView = new ArrayList<>(); for (int row : selectedRows) { String pdbIdStr = getResultTable() @@ -761,6 +760,7 @@ public class StructureChooser extends GStructureChooser pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries()); } + if (pdbEntry == null) { pdbEntry = new PDBEntry(); @@ -783,7 +783,7 @@ public class StructureChooser extends GStructureChooser .getModelIndex(); int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence") .getModelIndex(); - List selectedSeqsToView = new ArrayList(); + List selectedSeqsToView = new ArrayList<>(); for (int row : selectedRows) { PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row, @@ -805,7 +805,6 @@ public class StructureChooser extends GStructureChooser { selectedSequence = userSelectedSeq; } - String pdbIdStr = txt_search.getText(); PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr); if (pdbEntry == null) @@ -847,6 +846,7 @@ public class StructureChooser extends GStructureChooser { selectedSequence }); } closeAction(preferredHeight); + mainFrame.dispose(); } }).start(); } @@ -870,13 +870,15 @@ public class StructureChooser extends GStructureChooser final PDBEntry[] pdbEntriesToView, final AlignmentPanel alignPanel, SequenceI[] sequences) { - ssm.setProgressBar(MessageManager - .getString("status.launching_3d_structure_viewer")); + long progressId = sequences.hashCode(); + setProgressBar(MessageManager + .getString("status.launching_3d_structure_viewer"), progressId); final StructureViewer sViewer = new StructureViewer(ssm); + setProgressBar(null, progressId); if (SiftsSettings.isMapWithSifts()) { - List seqsWithoutSourceDBRef = new ArrayList(); + List seqsWithoutSourceDBRef = new ArrayList<>(); int p = 0; // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a // real PDB ID. For moment, we can also safely do this if there is already @@ -907,41 +909,37 @@ public class StructureChooser extends GStructureChooser if (!seqsWithoutSourceDBRef.isEmpty()) { int y = seqsWithoutSourceDBRef.size(); - ssm.setProgressBar(null); - ssm.setProgressBar(MessageManager.formatMessage( + setProgressBar(MessageManager.formatMessage( "status.fetching_dbrefs_for_sequences_without_valid_refs", - y)); + y), progressId); SequenceI[] seqWithoutSrcDBRef = new SequenceI[y]; int x = 0; for (SequenceI fSeq : seqsWithoutSourceDBRef) { seqWithoutSrcDBRef[x++] = fSeq; } + DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef); dbRefFetcher.fetchDBRefs(true); + + setProgressBar("Fetch complete.", progressId); // todo i18n } } if (pdbEntriesToView.length > 1) { - ArrayList seqsMap = new ArrayList(); - for (SequenceI seq : sequences) - { - seqsMap.add(new SequenceI[] { seq }); - } - SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]); - ssm.setProgressBar(null); - ssm.setProgressBar(MessageManager.getString( - "status.fetching_3d_structures_for_selected_entries")); - sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel); + setProgressBar(MessageManager.getString( + "status.fetching_3d_structures_for_selected_entries"), + progressId); + sViewer.viewStructures(pdbEntriesToView, sequences, alignPanel); } else { - ssm.setProgressBar(null); - ssm.setProgressBar(MessageManager.formatMessage( + setProgressBar(MessageManager.formatMessage( "status.fetching_3d_structures_for", - pdbEntriesToView[0].getId())); + pdbEntriesToView[0].getId()),progressId); sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel); } + setProgressBar(null, progressId); } /** @@ -1000,7 +998,7 @@ public class StructureChooser extends GStructureChooser String searchTerm = txt_search.getText().toLowerCase(); searchTerm = searchTerm.split(":")[0]; // System.out.println(">>>>> search term : " + searchTerm); - List wantedFields = new ArrayList(); + List wantedFields = new ArrayList<>(); FTSRestRequest pdbRequest = new FTSRestRequest(); pdbRequest.setAllowEmptySeq(false); pdbRequest.setResponseSize(1); @@ -1062,7 +1060,7 @@ public class StructureChooser extends GStructureChooser public PDBEntryTableModel(List pdbEntries) { - this.pdbEntries = new ArrayList(pdbEntries); + this.pdbEntries = new ArrayList<>(pdbEntries); } @Override diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java index e58b378..b142613 100644 --- a/src/jalview/gui/StructureViewer.java +++ b/src/jalview/gui/StructureViewer.java @@ -29,19 +29,24 @@ import jalview.structure.StructureSelectionManager; import java.awt.Rectangle; import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** - * proxy for handling structure viewers. - * - * this allows new views to be created with the currently configured viewer, the - * preferred viewer to be set/read and existing views created previously with a - * particular viewer to be recovered + * A proxy for handling structure viewers, that orchestrates adding selected + * structures, associated with sequences in Jalview, to an existing viewer, or + * opening a new one. Currently supports either Jmol or Chimera as the structure + * viewer. * * @author jprocter */ public class StructureViewer { + private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type "; + StructureSelectionManager ssm; public enum ViewerType @@ -49,6 +54,16 @@ public class StructureViewer JMOL, CHIMERA }; + /** + * Constructor + * + * @param structureSelectionManager + */ + public StructureViewer(StructureSelectionManager structureSelectionManager) + { + ssm = structureSelectionManager; + } + public ViewerType getViewerType() { String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY, @@ -61,135 +76,157 @@ public class StructureViewer Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name()); } - public StructureViewer( - StructureSelectionManager structureSelectionManager) - { - ssm = structureSelectionManager; - } - /** * View multiple PDB entries, each with associated sequences * * @param pdbs - * @param seqsForPdbs + * @param seqs * @param ap * @return */ public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs, - SequenceI[][] seqsForPdbs, AlignmentPanel ap) + SequenceI[] seqs, AlignmentPanel ap) { - JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqsForPdbs, ap); + JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap); if (viewer != null) { + /* + * user added structure to an existing viewer - all done + */ return viewer; } - return viewStructures(getViewerType(), pdbs, seqsForPdbs, ap); + + ViewerType viewerType = getViewerType(); + + Map seqsForPdbs = getSequencesForPdbs(pdbs, + seqs); + PDBEntry[] pdbsForFile = seqsForPdbs.keySet().toArray( + new PDBEntry[seqsForPdbs.size()]); + SequenceI[][] theSeqs = seqsForPdbs.values().toArray( + new SequenceI[seqsForPdbs.size()][]); + JalviewStructureDisplayI sview = null; + if (viewerType.equals(ViewerType.JMOL)) + { + sview = new AppJmol(ap, pdbsForFile, theSeqs); + } + else if (viewerType.equals(ViewerType.CHIMERA)) + { + sview = new ChimeraViewFrame(pdbsForFile, theSeqs, ap); + } + else + { + Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); + } + return sview; } /** - * A strictly temporary method pending JAL-1761 refactoring. Determines if all - * the passed PDB entries are the same (this is the case if selected sequences - * to view structure for are chains of the same structure). If so, calls the - * single-pdb version of viewStructures and returns the viewer, else returns - * null. + * Converts the list of selected PDB entries (possibly including duplicates + * for multiple chains), and corresponding sequences, into a map of sequences + * for each distinct PDB file. Returns null if either argument is null, or + * their lengths do not match. * * @param pdbs - * @param seqsForPdbs - * @param ap + * @param seqs * @return */ - private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs, - SequenceI[][] seqsForPdbs, AlignmentPanel ap) + Map getSequencesForPdbs(PDBEntry[] pdbs, + SequenceI[] seqs) { - List seqs = new ArrayList(); - if (pdbs == null || pdbs.length == 0) + if (pdbs == null || seqs == null || pdbs.length != seqs.length) { return null; } - int i = 0; - String firstFile = pdbs[0].getFile(); - for (PDBEntry pdb : pdbs) + + /* + * we want only one 'representative' PDBEntry per distinct file name + * (there may be entries for distinct chains) + */ + Map pdbsSeen = new HashMap<>(); + + /* + * LinkedHashMap preserves order of PDB entries (significant if they + * will get superimposed to the first structure) + */ + Map> pdbSeqs = new LinkedHashMap<>(); + for (int i = 0; i < pdbs.length; i++) { + PDBEntry pdb = pdbs[i]; + SequenceI seq = seqs[i]; String pdbFile = pdb.getFile(); - if (pdbFile == null || !pdbFile.equals(firstFile)) + if (!pdbsSeen.containsKey(pdbFile)) { - return null; + pdbsSeen.put(pdbFile, pdb); + pdbSeqs.put(pdb, new ArrayList()); + } + else + { + pdb = pdbsSeen.get(pdbFile); } - SequenceI[] pdbseqs = seqsForPdbs[i++]; - if (pdbseqs != null) + List seqsForPdb = pdbSeqs.get(pdb); + if (!seqsForPdb.contains(seq)) { - for (SequenceI sq : pdbseqs) - { - seqs.add(sq); - } + seqsForPdb.add(seq); } } - return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]), - ap); - } - - public JalviewStructureDisplayI viewStructures(PDBEntry pdb, - SequenceI[] seqsForPdb, AlignmentPanel ap) - { - return viewStructures(getViewerType(), pdb, seqsForPdb, ap); - } - protected JalviewStructureDisplayI viewStructures(ViewerType viewerType, - PDBEntry[] pdbs, SequenceI[][] seqsForPdbs, AlignmentPanel ap) - { - PDBEntry[] pdbsForFile = getUniquePdbFiles(pdbs); - JalviewStructureDisplayI sview = null; - if (viewerType.equals(ViewerType.JMOL)) - { - sview = new AppJmol(ap, pdbsForFile, - ap.av.collateForPDB(pdbsForFile)); - } - else if (viewerType.equals(ViewerType.CHIMERA)) + /* + * convert to Map + */ + Map result = new LinkedHashMap<>(); + for (Entry> entry : pdbSeqs.entrySet()) { - sview = new ChimeraViewFrame(pdbsForFile, - ap.av.collateForPDB(pdbsForFile), ap); + List theSeqs = entry.getValue(); + result.put(entry.getKey(), + theSeqs.toArray(new SequenceI[theSeqs.size()])); } - else - { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); - } - return sview; + + return result; } /** - * Convert the array of PDBEntry into an array with no filename repeated + * A strictly temporary method pending JAL-1761 refactoring. Determines if all + * the passed PDB entries are the same (this is the case if selected sequences + * to view structure for are chains of the same structure). If so, calls the + * single-pdb version of viewStructures and returns the viewer, else returns + * null. * * @param pdbs + * @param seqsForPdbs + * @param ap * @return */ - static PDBEntry[] getUniquePdbFiles(PDBEntry[] pdbs) + private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs, + SequenceI[] seqsForPdbs, AlignmentPanel ap) { - if (pdbs == null) + List seqs = new ArrayList(); + if (pdbs == null || pdbs.length == 0) { return null; } - List uniques = new ArrayList(); - List filesSeen = new ArrayList(); - for (PDBEntry entry : pdbs) + int i = 0; + String firstFile = pdbs[0].getFile(); + for (PDBEntry pdb : pdbs) { - String file = entry.getFile(); - if (file == null) + String pdbFile = pdb.getFile(); + if (pdbFile == null || !pdbFile.equals(firstFile)) { - uniques.add(entry); + return null; } - else if (!filesSeen.contains(file)) + SequenceI pdbseq = seqsForPdbs[i++]; + if (pdbseq != null) { - uniques.add(entry); - filesSeen.add(file); + seqs.add(pdbseq); } } - return uniques.toArray(new PDBEntry[uniques.size()]); + return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]), + ap); } - protected JalviewStructureDisplayI viewStructures(ViewerType viewerType, - PDBEntry pdb, SequenceI[] seqsForPdb, AlignmentPanel ap) + public JalviewStructureDisplayI viewStructures(PDBEntry pdb, + SequenceI[] seqsForPdb, AlignmentPanel ap) { + ViewerType viewerType = getViewerType(); JalviewStructureDisplayI sview = null; if (viewerType.equals(ViewerType.JMOL)) { @@ -201,8 +238,7 @@ public class StructureViewer } else { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); + Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); } return sview; } @@ -242,7 +278,7 @@ public class StructureViewer "Unsupported structure viewer type " + type.toString()); break; default: - Cache.log.error("Unknown structure viewer type " + type.toString()); + Cache.log.error(UNKNOWN_VIEWER_TYPE + type.toString()); } return sview; } diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index 3ba9947..31c20ed 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -83,18 +83,18 @@ public abstract class StructureViewerBase extends GStructureViewer /** * list of sequenceSet ids associated with the view */ - protected List _aps = new ArrayList(); + protected List _aps = new ArrayList<>(); /** * list of alignment panels to use for superposition */ - protected Vector _alignwith = new Vector(); + protected Vector _alignwith = new Vector<>(); /** * list of alignment panels that are used for colouring structures by aligned * sequences */ - protected Vector _colourwith = new Vector(); + protected Vector _colourwith = new Vector<>(); private String viewId = null; @@ -170,7 +170,7 @@ public abstract class StructureViewerBase extends GStructureViewer { if (_alignwith == null) { - _alignwith = new Vector(); + _alignwith = new Vector<>(); } if (_alignwith.size() == 0 && ap != null) { @@ -310,6 +310,8 @@ public abstract class StructureViewerBase extends GStructureViewer public abstract ViewerType getViewerType(); + protected abstract IProgressIndicator getIProgressIndicator(); + /** * add a new structure (with associated sequences and chains) to this viewer, * retrieving it if necessary first. @@ -460,7 +462,7 @@ public abstract class StructureViewerBase extends GStructureViewer * create the mappings */ apanel.getStructureSelectionManager().setMapping(seq, chains, - pdbFilename, DataSourceType.FILE); + pdbFilename, DataSourceType.FILE, getIProgressIndicator()); /* * alert the FeatureRenderer to show new (PDB RESNUM) features @@ -468,7 +470,9 @@ public abstract class StructureViewerBase extends GStructureViewer if (apanel.getSeqPanel().seqCanvas.fr != null) { apanel.getSeqPanel().seqCanvas.fr.featuresAdded(); - apanel.paintAlignment(true); + // note - we don't do a refresh for structure here because we do it + // explicitly for all panels later on + apanel.paintAlignment(true, false); } /* @@ -717,11 +721,11 @@ public abstract class StructureViewerBase extends GStructureViewer if (_colourwith == null) { - _colourwith = new Vector(); + _colourwith = new Vector<>(); } if (_alignwith == null) { - _alignwith = new Vector(); + _alignwith = new Vector<>(); } ViewSelectionMenu seqColourBy = new ViewSelectionMenu( @@ -888,7 +892,7 @@ public abstract class StructureViewerBase extends GStructureViewer binding.setColourBySequence(seqColour.isSelected()); if (_colourwith == null) { - _colourwith = new Vector(); + _colourwith = new Vector<>(); } if (binding.isColourBySequence()) { diff --git a/src/jalview/gui/TextColourChooser.java b/src/jalview/gui/TextColourChooser.java index 3986561..53e2dee 100644 --- a/src/jalview/gui/TextColourChooser.java +++ b/src/jalview/gui/TextColourChooser.java @@ -184,9 +184,9 @@ public class TextColourChooser */ protected void saveInitialSettings() { - groupColour1 = new HashMap(); - groupColour2 = new HashMap(); - groupThreshold = new HashMap(); + groupColour1 = new HashMap<>(); + groupColour2 = new HashMap<>(); + groupThreshold = new HashMap<>(); if (sg == null) { @@ -237,7 +237,7 @@ public class TextColourChooser sg.textColour = col; } - ap.paintAlignment(true); + ap.paintAlignment(false, false); } void colour2Changed(Color col) @@ -255,7 +255,7 @@ public class TextColourChooser sg.textColour2 = col; } - ap.paintAlignment(true); + ap.paintAlignment(false, false); } void thresholdChanged(int value) @@ -273,7 +273,7 @@ public class TextColourChooser sg.thresholdTextColour = value; } - ap.paintAlignment(true); + ap.paintAlignment(false, false); } void setGroupTextColour() diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index 5e14fce..2727db1 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -55,6 +55,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; @@ -63,6 +64,8 @@ import javax.imageio.ImageIO; import javax.swing.ButtonGroup; import javax.swing.JMenuItem; import javax.swing.JRadioButtonMenuItem; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; import org.jibble.epsgraphics.EpsGraphics2D; @@ -141,7 +144,35 @@ public class TreePanel extends GTreePanel buildAssociatedViewMenu(); - av.addPropertyChangeListener(new java.beans.PropertyChangeListener() + final PropertyChangeListener listener = addAlignmentListener(); + + /* + * remove listener when window is closed, so that this + * panel can be garbage collected + */ + addInternalFrameListener(new InternalFrameAdapter() + { + @Override + public void internalFrameClosed(InternalFrameEvent evt) + { + if (av != null) + { + av.removePropertyChangeListener(listener); + } + } + }); + + TreeLoader tl = new TreeLoader(newTree, inputData); + tl.start(); + + } + + /** + * @return + */ + protected PropertyChangeListener addAlignmentListener() + { + final PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) @@ -168,11 +199,9 @@ public class TreePanel extends GTreePanel repaint(); } } - }); - - TreeLoader tl = new TreeLoader(newTree, inputData); - tl.start(); - + }; + av.addPropertyChangeListener(listener); + return listener; } @Override @@ -493,7 +522,7 @@ public class TreePanel extends GTreePanel if (treeCanvas.applyToAllViews) { - final ArrayList commands = new ArrayList(); + final ArrayList commands = new ArrayList<>(); for (AlignmentPanel ap : PaintRefresher .getAssociatedPanels(av.getSequenceSetId())) { @@ -550,13 +579,14 @@ public class TreePanel extends GTreePanel public CommandI sortAlignmentIn(AlignmentPanel ap) { + // TODO: move to alignment view controller AlignmentViewport viewport = ap.av; SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray(); AlignmentSorter.sortByTree(viewport.getAlignment(), tree); CommandI undo; undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment()); - ap.paintAlignment(true); + ap.paintAlignment(true, false); return undo; } diff --git a/src/jalview/gui/UserDefinedColours.java b/src/jalview/gui/UserDefinedColours.java index 8b45c40..3290500 100755 --- a/src/jalview/gui/UserDefinedColours.java +++ b/src/jalview/gui/UserDefinedColours.java @@ -136,7 +136,7 @@ public class UserDefinedColours extends GUserDefinedColours UserDefinedColours() { super(); - selectedButtons = new ArrayList(); + selectedButtons = new ArrayList<>(); } void showFrame() @@ -163,7 +163,7 @@ public class UserDefinedColours extends GUserDefinedColours if (upperCaseButtons == null) { - upperCaseButtons = new ArrayList(); + upperCaseButtons = new ArrayList<>(); } for (int i = 0; i < 20; i++) @@ -194,7 +194,7 @@ public class UserDefinedColours extends GUserDefinedColours if (lowerCaseButtons == null) { - lowerCaseButtons = new ArrayList(); + lowerCaseButtons = new ArrayList<>(); } for (int i = 0; i < 20; i++) @@ -631,8 +631,8 @@ public class UserDefinedColours extends GUserDefinedColours @Override protected void loadbutton_actionPerformed() { - upperCaseButtons = new ArrayList(); - lowerCaseButtons = new ArrayList(); + upperCaseButtons = new ArrayList<>(); + lowerCaseButtons = new ArrayList<>(); JalviewFileChooser chooser = new JalviewFileChooser("jc", "Jalview User Colours"); @@ -876,7 +876,7 @@ public class UserDefinedColours extends GUserDefinedColours protected void cancelButton_actionPerformed() { ap.alignFrame.changeColour(oldColourScheme); - ap.paintAlignment(true); + ap.paintAlignment(true, true); try { diff --git a/src/jalview/gui/ViewSelectionMenu.java b/src/jalview/gui/ViewSelectionMenu.java index cdbb4fa..2a7743a 100644 --- a/src/jalview/gui/ViewSelectionMenu.java +++ b/src/jalview/gui/ViewSelectionMenu.java @@ -60,15 +60,6 @@ public class ViewSelectionMenu extends JMenu private ItemListener _handler; - @Override - protected void finalize() throws Throwable - { - _selectedviews = null; - _handler = null; - _allviews = null; - super.finalize(); - } - /** * create a new view selection menu. This menu has some standard entries * (select all, invert selection), and a checkbox for every view. Mousing over diff --git a/src/jalview/io/AlignFile.java b/src/jalview/io/AlignFile.java index a603cca..2340283 100755 --- a/src/jalview/io/AlignFile.java +++ b/src/jalview/io/AlignFile.java @@ -174,11 +174,6 @@ public abstract class AlignFile extends FileParse } parseCalled = true; parse(); - // sets the index of each sequence in the alignment - for (int i = 0, c = seqs.size(); i < c; i++) - { - seqs.get(i).setIndex(i); - } } /** diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index 26641b1..f26d6da 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -606,18 +606,4 @@ public class FileLoader implements Runnable return tempStructFile.toString(); } - /* - * (non-Javadoc) - * - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() throws Throwable - { - source = null; - alignFrame = null; - viewport = null; - super.finalize(); - } - } diff --git a/src/jalview/io/InputStreamParser.java b/src/jalview/io/InputStreamParser.java index d269e97..65ba74a 100644 --- a/src/jalview/io/InputStreamParser.java +++ b/src/jalview/io/InputStreamParser.java @@ -47,11 +47,4 @@ public class InputStreamParser extends FileParse error = false; } - @Override - protected void finalize() throws Throwable - { - dataIn = null; - super.finalize(); - } - } diff --git a/src/jalview/io/WSWUBlastClient.java b/src/jalview/io/WSWUBlastClient.java index 6e7df71..d526a31 100755 --- a/src/jalview/io/WSWUBlastClient.java +++ b/src/jalview/io/WSWUBlastClient.java @@ -184,7 +184,7 @@ public class WSWUBlastClient } } } - ap.paintAlignment(true); + ap.paintAlignment(true, false); } diff --git a/src/jalview/io/cache/JvCacheableInputBox.java b/src/jalview/io/cache/JvCacheableInputBox.java index 3d0daed..a837512 100644 --- a/src/jalview/io/cache/JvCacheableInputBox.java +++ b/src/jalview/io/cache/JvCacheableInputBox.java @@ -28,6 +28,7 @@ import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -70,11 +71,49 @@ public class JvCacheableInputBox extends JComboBox private JMenuItem menuItemClearCache = new JMenuItem(); + volatile boolean enterWasPressed = false; + + /** + * @return flag indicating if the most recent keypress was enter + */ + public boolean wasEnterPressed() + { + return enterWasPressed; + } + public JvCacheableInputBox(String newCacheKey) { super(); this.cacheKey = newCacheKey; setEditable(true); + addKeyListener(new KeyListener() + { + + @Override + public void keyTyped(KeyEvent e) + { + enterWasPressed = false; + if (e.getKeyCode() == KeyEvent.VK_ENTER) + { + enterWasPressed = true; + } + // let event bubble up + } + + @Override + public void keyReleased(KeyEvent e) + { + // TODO Auto-generated method stub + + } + + @Override + public void keyPressed(KeyEvent e) + { + // TODO Auto-generated method stub + + } + }); setPrototypeDisplayValue( "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); appCache = AppCache.getInstance(); diff --git a/src/jalview/javascript/JSFunctionExec.java b/src/jalview/javascript/JSFunctionExec.java index 083cd26..29f3fa9 100644 --- a/src/jalview/javascript/JSFunctionExec.java +++ b/src/jalview/javascript/JSFunctionExec.java @@ -39,19 +39,6 @@ public class JSFunctionExec implements Runnable jvlite.setExecutor(this); } - @Override - protected void finalize() throws Throwable - { - jvlite = null; - executor = null; - if (jsExecQueue != null) - { - jsExecQueue.clear(); - } - jsExecQueue = null; - super.finalize(); - } - private Vector jsExecQueue; private Thread executor = null; diff --git a/src/jalview/javascript/MouseOverStructureListener.java b/src/jalview/javascript/MouseOverStructureListener.java index 874bfd3..6071933 100644 --- a/src/jalview/javascript/MouseOverStructureListener.java +++ b/src/jalview/javascript/MouseOverStructureListener.java @@ -299,13 +299,6 @@ public class MouseOverStructureListener extends JSFunctionExec } @Override - public void finalize() throws Throwable - { - jvlite = null; - super.finalize(); - } - - @Override public void releaseReferences(Object svl) { diff --git a/src/jalview/renderer/ScaleRenderer.java b/src/jalview/renderer/ScaleRenderer.java index 9fec256..d92608c 100644 --- a/src/jalview/renderer/ScaleRenderer.java +++ b/src/jalview/renderer/ScaleRenderer.java @@ -34,12 +34,24 @@ import java.util.List; */ public class ScaleRenderer { + /** + * Represents one major or minor scale mark + */ public final class ScaleMark { + /** + * true for a major scale mark, false for minor + */ public final boolean major; + /** + * visible column position (0..) e.g. 19 + */ public final int column; + /** + * text (if any) to show e.g. "20" + */ public final String text; ScaleMark(boolean isMajor, int col, String txt) @@ -48,19 +60,27 @@ public class ScaleRenderer column = col; text = txt; } + + /** + * String representation for inspection when debugging only + */ + @Override + public String toString() + { + return String.format("%s:%d:%s", major ? "major" : "minor", column, + text); + } } /** - * calculate positions markers on the alignment ruler + * Calculates position markers on the alignment ruler * * @param av * @param startx - * left-most column in visible view + * left-most column in visible view (0..) * @param endx - * - right-most column in visible view - * @return List of ScaleMark holding boolean: true/false for major/minor mark, - * marker position in alignment column coords, a String to be rendered - * at the position (or null) + * - right-most column in visible view (0..) + * @return */ public List calculateMarks(AlignViewportI av, int startx, int endx) @@ -87,41 +107,40 @@ public class ScaleRenderer scalestartx += 5; } List marks = new ArrayList(); - String string; - int refN, iadj; // todo: add a 'reference origin column' to set column number relative to - for (int i = scalestartx; i < endx; i += 5) + for (int i = scalestartx; i <= endx; i += 5) { if (((i - refSp) % 10) == 0) { + String text; if (refSeq == null) { - iadj = av.getAlignment().getHiddenColumns() + int iadj = av.getAlignment().getHiddenColumns() .adjustForHiddenColumns(i - 1) + 1; - string = String.valueOf(iadj); + text = String.valueOf(iadj); } else { - iadj = av.getAlignment().getHiddenColumns() + int iadj = av.getAlignment().getHiddenColumns() .adjustForHiddenColumns(i - 1); - refN = refSeq.findPosition(iadj); + int refN = refSeq.findPosition(iadj); // TODO show bounds if position is a gap // - ie L--R -> "1L|2R" for // marker if (iadj < refStartI) { - string = String.valueOf(iadj - refStartI); + text = String.valueOf(iadj - refStartI); } else if (iadj > refEndI) { - string = "+" + String.valueOf(iadj - refEndI); + text = "+" + String.valueOf(iadj - refEndI); } else { - string = String.valueOf(refN) + refSeq.getCharAt(iadj); + text = String.valueOf(refN) + refSeq.getCharAt(iadj); } } - marks.add(new ScaleMark(true, i - startx - 1, string)); + marks.add(new ScaleMark(true, i - startx - 1, text)); } else { diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index e81e519..1f47da3 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -216,7 +216,8 @@ public class FeatureRenderer extends FeatureRendererModel return null; } - if (Comparison.isGap(seq.getCharAt(column))) + // column is 'base 1' but getCharAt is an array index (ie from 0) + if (Comparison.isGap(seq.getCharAt(column - 1))) { /* * returning null allows the colour scheme to provide gap colour @@ -401,27 +402,6 @@ public class FeatureRenderer extends FeatureRendererModel } /** -<<<<<<< HEAD -======= - * Answers true if the feature belongs to a feature group which is not - * currently displayed, else false - * - * @param sequenceFeature - * @return - */ - @Override - protected boolean featureGroupNotShown( - final SequenceFeature sequenceFeature) - { - return featureGroups != null && sequenceFeature.featureGroup != null - && sequenceFeature.featureGroup.length() != 0 - && featureGroups.containsKey(sequenceFeature.featureGroup) - && !featureGroups.get(sequenceFeature.featureGroup) - .booleanValue(); - } - - /** ->>>>>>> refs/heads/develop * Called when alignment in associated view has new/modified features to * discover and display. * diff --git a/src/jalview/schemes/RNAHelicesColourChooser.java b/src/jalview/schemes/RNAHelicesColourChooser.java index 15cb157..9809fa9 100644 --- a/src/jalview/schemes/RNAHelicesColourChooser.java +++ b/src/jalview/schemes/RNAHelicesColourChooser.java @@ -58,7 +58,7 @@ public class RNAHelicesColourChooser oldcs = av.getGlobalColourScheme(); if (av.getAlignment().getGroups() != null) { - oldgroupColours = new Hashtable(); + oldgroupColours = new Hashtable<>(); for (SequenceGroup sg : ap.getAlignment().getGroups()) { if (sg.getColourScheme() != null) @@ -71,7 +71,7 @@ public class RNAHelicesColourChooser this.ap = ap; adjusting = true; - Vector list = new Vector(); + Vector list = new Vector<>(); int index = 1; AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation(); if (anns != null) @@ -105,6 +105,6 @@ public class RNAHelicesColourChooser av.setGlobalColourScheme(rhc); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index b973f45..35e2536 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -66,7 +66,7 @@ public class StructureSelectionManager static IdentityHashMap instances; - private List mappings = new ArrayList(); + private List mappings = new ArrayList<>(); private boolean processSecondaryStructure = false; @@ -74,20 +74,16 @@ public class StructureSelectionManager private boolean addTempFacAnnot = false; - private IProgressIndicator progressIndicator; - private SiftsClient siftsClient = null; - private long progressSessionId; - /* * Set of any registered mappings between (dataset) sequences. */ - private List seqmappings = new ArrayList(); + private List seqmappings = new ArrayList<>(); - private List commandListeners = new ArrayList(); + private List commandListeners = new ArrayList<>(); - private List sel_listeners = new ArrayList(); + private List sel_listeners = new ArrayList<>(); /** * @return true if will try to use external services for processing secondary @@ -175,9 +171,9 @@ public class StructureSelectionManager * map between the PDB IDs (or structure identifiers) used by Jalview and the * absolute filenames for PDB data that corresponds to it */ - Map pdbIdFileName = new HashMap(); + Map pdbIdFileName = new HashMap<>(); - Map pdbFileNameId = new HashMap(); + Map pdbFileNameId = new HashMap<>(); public void registerPDBFile(String idForFile, String absoluteFile) { @@ -228,7 +224,7 @@ public class StructureSelectionManager } if (instances == null) { - instances = new java.util.IdentityHashMap(); + instances = new java.util.IdentityHashMap<>(); } StructureSelectionManager instance = instances.get(context); if (instance == null) @@ -324,9 +320,11 @@ public class StructureSelectionManager * @return null or the structure data parsed as a pdb file */ synchronized public StructureFile setMapping(SequenceI[] sequence, - String[] targetChains, String pdbFile, DataSourceType protocol) + String[] targetChains, String pdbFile, DataSourceType protocol, + IProgressIndicator progress) { - return setMapping(true, sequence, targetChains, pdbFile, protocol); + return computeMapping(true, sequence, targetChains, pdbFile, protocol, + progress); } /** @@ -353,6 +351,16 @@ public class StructureSelectionManager SequenceI[] sequenceArray, String[] targetChainIds, String pdbFile, DataSourceType sourceType) { + return computeMapping(forStructureView, sequenceArray, targetChainIds, + pdbFile, sourceType, null); + } + + synchronized public StructureFile computeMapping( + boolean forStructureView, SequenceI[] sequenceArray, + String[] targetChainIds, String pdbFile, DataSourceType sourceType, + IProgressIndicator progress) + { + long progressSessionId = System.currentTimeMillis() * 3; /* * There will be better ways of doing this in the future, for now we'll use * the tried and tested MCview pdb mapping @@ -500,12 +508,14 @@ public class StructureSelectionManager pdbFile = "INLINE" + pdb.getId(); } - List seqToStrucMapping = new ArrayList(); + List seqToStrucMapping = new ArrayList<>(); if (isMapUsingSIFTs && seq.isProtein()) { - setProgressBar(null); - setProgressBar(MessageManager - .getString("status.obtaining_mapping_with_sifts")); + if (progress!=null) { + progress.setProgressBar(MessageManager + .getString("status.obtaining_mapping_with_sifts"), + progressSessionId); + } jalview.datamodel.Mapping sqmpping = maxAlignseq .getMappingFromS1(false); if (targetChainId != null && !targetChainId.trim().isEmpty()) @@ -538,7 +548,7 @@ public class StructureSelectionManager } else { - List foundSiftsMappings = new ArrayList(); + List foundSiftsMappings = new ArrayList<>(); for (PDBChain chain : pdb.getChains()) { try @@ -575,20 +585,25 @@ public class StructureSelectionManager } else { - setProgressBar(null); - setProgressBar(MessageManager - .getString("status.obtaining_mapping_with_nw_alignment")); + if (progress != null) + { + progress.setProgressBar(MessageManager + .getString("status.obtaining_mapping_with_nw_alignment"), + progressSessionId); + } StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId, maxChain, pdb, maxAlignseq); seqToStrucMapping.add(nwMapping); ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); - } - if (forStructureView) { mappings.addAll(seqToStrucMapping); } + if (progress != null) + { + progress.setProgressBar(null, progressSessionId); + } } return pdb; } @@ -683,7 +698,7 @@ public class StructureSelectionManager .getMappingFromS1(false); maxChain.transferRESNUMFeatures(seq, null); - HashMap mapping = new HashMap(); + HashMap mapping = new HashMap<>(); int resNum = -10000; int index = 0; char insCode = ' '; @@ -737,7 +752,7 @@ public class StructureSelectionManager * Remove mappings to the closed listener's PDB files, but first check if * another listener is still interested */ - List pdbs = new ArrayList(Arrays.asList(pdbfiles)); + List pdbs = new ArrayList<>(Arrays.asList(pdbfiles)); StructureListener sl; for (int i = 0; i < listeners.size(); i++) @@ -758,7 +773,7 @@ public class StructureSelectionManager */ if (pdbs.size() > 0) { - List tmp = new ArrayList(); + List tmp = new ArrayList<>(); for (StructureMapping sm : mappings) { if (!pdbs.contains(sm.pdbfile)) @@ -844,7 +859,7 @@ public class StructureSelectionManager && sm.pdbchain.equals(atom.getChain())) { int indexpos = sm.getSeqPos(atom.getPdbResNum()); - if (lastipos != indexpos && lastseq != sm.sequence) + if (lastipos != indexpos || lastseq != sm.sequence) { results.addResult(sm.sequence, indexpos, indexpos); lastipos = indexpos; @@ -952,7 +967,7 @@ public class StructureSelectionManager return; } int atomNo; - List atoms = new ArrayList(); + List atoms = new ArrayList<>(); for (StructureMapping sm : mappings) { if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence() @@ -1060,7 +1075,7 @@ public class StructureSelectionManager public StructureMapping[] getMapping(String pdbfile) { - List tmp = new ArrayList(); + List tmp = new ArrayList<>(); for (StructureMapping sm : mappings) { if (sm.pdbfile.equals(pdbfile)) @@ -1220,7 +1235,7 @@ public class StructureSelectionManager } } - Vector view_listeners = new Vector(); + Vector view_listeners = new Vector<>(); public synchronized void sendViewPosition( jalview.api.AlignmentViewPanel source, int startRes, int endRes, @@ -1343,35 +1358,6 @@ public class StructureSelectionManager return null; } - public IProgressIndicator getProgressIndicator() - { - return progressIndicator; - } - - public void setProgressIndicator(IProgressIndicator progressIndicator) - { - this.progressIndicator = progressIndicator; - } - - public long getProgressSessionId() - { - return progressSessionId; - } - - public void setProgressSessionId(long progressSessionId) - { - this.progressSessionId = progressSessionId; - } - - public void setProgressBar(String message) - { - if (progressIndicator == null) - { - return; - } - progressIndicator.setProgressBar(message, progressSessionId); - } - public List getSequenceMappings() { return seqmappings; diff --git a/src/jalview/urls/CustomUrlProvider.java b/src/jalview/urls/CustomUrlProvider.java index 0d5ef99..86d5660 100644 --- a/src/jalview/urls/CustomUrlProvider.java +++ b/src/jalview/urls/CustomUrlProvider.java @@ -160,10 +160,24 @@ public class CustomUrlProvider extends UrlProviderImpl */ private void upgradeOldLinks(HashMap urls) { + boolean upgrade = false; // upgrade old SRS link if (urls.containsKey(SRS_LABEL)) { urls.remove(SRS_LABEL); + upgrade = true; + } + // upgrade old EBI link - easier just to remove and re-add than faffing + // around checking exact url + if (urls.containsKey(UrlConstants.DEFAULT_LABEL)) + { + // note because this is called separately for selected and nonselected + // urls, the default url will not always be present + urls.remove(UrlConstants.DEFAULT_LABEL); + upgrade = true; + } + if (upgrade) + { UrlLink link = new UrlLink(UrlConstants.DEFAULT_STRING); link.setLabel(UrlConstants.DEFAULT_LABEL); urls.put(UrlConstants.DEFAULT_LABEL, link); diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java index 3682239..9c5c109 100644 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@ -939,4 +939,55 @@ public final class MappingUtils } return copy; } + + /** + * Removes the specified number of positions from the given ranges. Provided + * to allow a stop codon to be stripped from a CDS sequence so that it matches + * the peptide translation length. + * + * @param positions + * @param ranges + * a list of (single) [start, end] ranges + * @return + */ + public static void removeEndPositions(int positions, + List ranges) + { + int toRemove = positions; + Iterator it = new ReverseListIterator<>(ranges); + while (toRemove > 0) + { + int[] endRange = it.next(); + if (endRange.length != 2) + { + /* + * not coded for [start1, end1, start2, end2, ...] + */ + System.err + .println("MappingUtils.removeEndPositions doesn't handle multiple ranges"); + return; + } + + int length = endRange[1] - endRange[0] + 1; + if (length <= 0) + { + /* + * not coded for a reverse strand range (end < start) + */ + System.err + .println("MappingUtils.removeEndPositions doesn't handle reverse strand"); + return; + } + if (length > toRemove) + { + endRange[1] -= toRemove; + toRemove = 0; + } + else + { + toRemove -= length; + it.remove(); + } + } + } } diff --git a/src/jalview/util/UrlConstants.java b/src/jalview/util/UrlConstants.java index d6ece8d..e5cfaee 100644 --- a/src/jalview/util/UrlConstants.java +++ b/src/jalview/util/UrlConstants.java @@ -57,10 +57,20 @@ public class UrlConstants public static final String DEFAULT_STRING = DEFAULT_LABEL + "|https://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$"; + private static final String COLON = ":"; + /* * not instantiable */ private UrlConstants() { } + + public static boolean isDefaultString(String link) + { + String sublink = link.substring(link.indexOf(COLON) + 1); + String subdefault = DEFAULT_STRING + .substring(DEFAULT_STRING.indexOf(COLON) + 1); + return sublink.equalsIgnoreCase(subdefault); + } } diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 3702cd0..a0cbff4 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -22,6 +22,7 @@ package jalview.viewmodel; import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.analysis.Conservation; +import jalview.analysis.TreeModel; import jalview.api.AlignCalcManagerI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; @@ -33,7 +34,6 @@ import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.Annotation; -import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenColumns; import jalview.datamodel.HiddenSequences; @@ -80,7 +80,7 @@ import java.util.Map; public abstract class AlignmentViewport implements AlignViewportI, CommandListener, VamsasSource { - final protected ViewportRanges ranges; + protected ViewportRanges ranges; protected ViewStyleI viewStyle = new ViewStyle(); @@ -948,11 +948,15 @@ public abstract class AlignmentViewport groupConsensus = null; groupConservation = null; hconsensus = null; + hconservation = null; hcomplementConsensus = null; - // colour scheme may hold reference to consensus - residueShading = null; - // TODO remove listeners from changeSupport? + gapcounts = null; + calculator = null; + residueShading = null; // may hold a reference to Consensus changeSupport = null; + ranges = null; + currentTree = null; + selectionGroup = null; setAlignment(null); } @@ -1334,7 +1338,10 @@ public abstract class AlignmentViewport public void removePropertyChangeListener( java.beans.PropertyChangeListener listener) { - changeSupport.removePropertyChangeListener(listener); + if (changeSupport != null) + { + changeSupport.removePropertyChangeListener(listener); + } } /** @@ -1673,13 +1680,6 @@ public abstract class AlignmentViewport } @Override - public CigarArray getViewAsCigars(boolean selectedRegionOnly) - { - return new CigarArray(alignment, alignment.getHiddenColumns(), - (selectedRegionOnly ? selectionGroup : null)); - } - - @Override public jalview.datamodel.AlignmentView getAlignmentView( boolean selectedOnly) { @@ -2877,6 +2877,8 @@ public abstract class AlignmentViewport */ private SearchResultsI searchResults = null; + protected TreeModel currentTree = null; + @Override public boolean hasSearchResults() { @@ -2935,4 +2937,16 @@ public abstract class AlignmentViewport + ((ignoreGapsInConsensusCalculation) ? " without gaps" : "")); return sq; } + + @Override + public void setCurrentTree(TreeModel tree) + { + currentTree = tree; + } + + @Override + public TreeModel getCurrentTree() + { + return currentTree; + } } diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index 42d490e..24ff57f 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -402,23 +402,39 @@ public class ViewportRanges extends ViewportProperties */ public boolean scrollUp(boolean up) { + /* + * if in unwrapped mode, scroll up or down one sequence row; + * if in wrapped mode, scroll by one visible width of columns + */ if (up) { - if (startSeq < 1) + if (wrappedMode) { - return false; + pageUp(); + } + else + { + if (startSeq < 1) + { + return false; + } + setStartSeq(startSeq - 1); } - - setStartSeq(startSeq - 1); } else { - if (endSeq >= getVisibleAlignmentHeight() - 1) + if (wrappedMode) { - return false; + pageDown(); + } + else + { + if (endSeq >= getVisibleAlignmentHeight() - 1) + { + return false; + } + setStartSeq(startSeq + 1); } - - setStartSeq(startSeq + 1); } return true; } diff --git a/src/jalview/workers/AnnotationWorker.java b/src/jalview/workers/AnnotationWorker.java index 8569039..8f37f15 100644 --- a/src/jalview/workers/AnnotationWorker.java +++ b/src/jalview/workers/AnnotationWorker.java @@ -51,7 +51,7 @@ class AnnotationWorker extends AlignCalcWorker AnnotationProviderI counter) { super(viewport, panel); - ourAnnots = new ArrayList(); + ourAnnots = new ArrayList<>(); this.counter = counter; calcMan.registerWorker(this); } @@ -121,7 +121,10 @@ class AnnotationWorker extends AlignCalcWorker if (ap != null) { ap.adjustAnnotationHeight(); - ap.paintAlignment(true); + // TODO: only need to update colour and geometry if panel height changes + // and view is coloured by annotation, and the annotation is actually + // changed! + ap.paintAlignment(true, true); } } diff --git a/src/jalview/workers/ColumnCounterSetWorker.java b/src/jalview/workers/ColumnCounterSetWorker.java index 24cb717..74695fe 100644 --- a/src/jalview/workers/ColumnCounterSetWorker.java +++ b/src/jalview/workers/ColumnCounterSetWorker.java @@ -57,7 +57,7 @@ class ColumnCounterSetWorker extends AlignCalcWorker AlignmentViewPanel panel, FeatureSetCounterI counter) { super(viewport, panel); - ourAnnots = new ArrayList(); + ourAnnots = new ArrayList<>(); this.counter = counter; calcMan.registerWorker(this); } @@ -116,7 +116,7 @@ class ColumnCounterSetWorker extends AlignCalcWorker { ap.adjustAnnotationHeight(); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/workers/ConsensusThread.java b/src/jalview/workers/ConsensusThread.java index 4242b2a..335529c 100644 --- a/src/jalview/workers/ConsensusThread.java +++ b/src/jalview/workers/ConsensusThread.java @@ -64,7 +64,7 @@ public class ConsensusThread extends AlignCalcWorker { if (ap != null) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); } Thread.sleep(200); } catch (Exception ex) @@ -93,7 +93,7 @@ public class ConsensusThread extends AlignCalcWorker if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } catch (OutOfMemoryError error) { diff --git a/src/jalview/workers/ConservationThread.java b/src/jalview/workers/ConservationThread.java index 571234c..54b0191 100644 --- a/src/jalview/workers/ConservationThread.java +++ b/src/jalview/workers/ConservationThread.java @@ -75,7 +75,7 @@ public class ConservationThread extends AlignCalcWorker abortAndDestroy(); return; } - List ourAnnot = new ArrayList(); + List ourAnnot = new ArrayList<>(); AlignmentI alignment = alignViewport.getAlignment(); conservation = alignViewport.getAlignmentConservationAnnotation(); quality = alignViewport.getAlignmentQualityAnnot(); @@ -123,7 +123,7 @@ public class ConservationThread extends AlignCalcWorker } if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/workers/StrucConsensusThread.java b/src/jalview/workers/StrucConsensusThread.java index 5ed2885..61ec3d0 100644 --- a/src/jalview/workers/StrucConsensusThread.java +++ b/src/jalview/workers/StrucConsensusThread.java @@ -139,7 +139,7 @@ public class StrucConsensusThread extends AlignCalcWorker calcMan.workerComplete(this); if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/ws/DasSequenceFeatureFetcher.java b/src/jalview/ws/DasSequenceFeatureFetcher.java index 0a61dff..c661e2c 100644 --- a/src/jalview/ws/DasSequenceFeatureFetcher.java +++ b/src/jalview/ws/DasSequenceFeatureFetcher.java @@ -135,7 +135,7 @@ public class DasSequenceFeatureFetcher boolean useJDasMultiThread) { this.useJDASMultiThread = useJDasMultiThread; - this.selectedSources = new ArrayList(); + this.selectedSources = new ArrayList<>(); // filter both sequences and sources to eliminate duplicates for (jalviewSourceI src : selectedSources2) { @@ -316,17 +316,17 @@ public class DasSequenceFeatureFetcher FeaturesClientMultipleSources fc = new FeaturesClientMultipleSources(); fc.setConnProps(sourceRegistry.getSessionHandler()); // Now sending requests one at a time to each server - ArrayList srcobj = new ArrayList(); - ArrayList src = new ArrayList(); - List> ids = new ArrayList>(); - List> idobj = new ArrayList>(); - List> sqset = new ArrayList>(); + ArrayList srcobj = new ArrayList<>(); + ArrayList src = new ArrayList<>(); + List> ids = new ArrayList<>(); + List> idobj = new ArrayList<>(); + List> sqset = new ArrayList<>(); for (jalviewSourceI _sr : selectedSources) { - Map slist = new HashMap(); - List idob = new ArrayList(); - List qset = new ArrayList(); + Map slist = new HashMap<>(); + List idob = new ArrayList<>(); + List qset = new ArrayList<>(); for (SequenceI seq : sequences) { @@ -368,8 +368,8 @@ public class DasSequenceFeatureFetcher sqset.add(slist); } } - Map, Exception>> errors = new HashMap, Exception>>(); - Map, DasGFFAdapter>> results = new HashMap, DasGFFAdapter>>(); + Map, Exception>> errors = new HashMap<>(); + Map, DasGFFAdapter>> results = new HashMap<>(); if (!useJDASMultiThread) { Iterator sources = src.iterator(); @@ -390,7 +390,7 @@ public class DasSequenceFeatureFetcher if (ers == null) { results.put(source, - ers = new HashMap, DasGFFAdapter>()); + ers = new HashMap<>()); } ers.put(qid, dga); } catch (Exception ex) @@ -399,7 +399,7 @@ public class DasSequenceFeatureFetcher if (ers == null) { errors.put(source, - ers = new HashMap, Exception>()); + ers = new HashMap<>()); } ers.put(qid, ex); } @@ -438,7 +438,7 @@ public class DasSequenceFeatureFetcher Map, DasGFFAdapter> results, Map, Exception> errors) { - Set sequences = new HashSet(); + Set sequences = new HashSet<>(); String source = jvsource.getSourceURL(); // process features DasGFFAdapter result = (results == null) ? null : results.get(ids); @@ -622,7 +622,7 @@ public class DasSequenceFeatureFetcher if (seq == af.getViewport().getAlignment().getSequenceAt(index) .getDatasetSequence()) { - af.alignPanel.paintAlignment(true); + af.alignPanel.paintAlignment(true, true); index = end; break; } @@ -647,8 +647,8 @@ public class DasSequenceFeatureFetcher // TODO: minimal list of DAS queries to make by querying with untyped ID if // distinct from any typed IDs - List ids = new ArrayList(); - List qstring = new ArrayList(); + List ids = new ArrayList<>(); + List qstring = new ArrayList<>(); boolean dasCoordSysFound = false; if (uprefs != null) diff --git a/src/jalview/ws/jws2/AADisorderClient.java b/src/jalview/ws/jws2/AADisorderClient.java index c24ea05..9a2316c 100644 --- a/src/jalview/ws/jws2/AADisorderClient.java +++ b/src/jalview/ws/jws2/AADisorderClient.java @@ -99,22 +99,22 @@ public class AADisorderClient extends JabawsCalcWorker { // TODO: turn this into some kind of configuration file that's a bit easier // to edit - featureMap = new HashMap>(); + featureMap = new HashMap<>(); Map fmap; featureMap.put(compbio.ws.client.Services.IUPredWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); fmap.put("Glob", new String[] { "Globular Domain", "Predicted globular domain" }); featureMap.put(compbio.ws.client.Services.JronnWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); featureMap.put(compbio.ws.client.Services.DisemblWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); fmap.put("REM465", new String[] { "REM465", "Missing density" }); fmap.put("HOTLOOPS", new String[] { "HOTLOOPS", "Flexible loops" }); fmap.put("COILS", new String[] { "COILS", "Random coil" }); featureMap.put(compbio.ws.client.Services.GlobPlotWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); fmap.put("GlobDoms", new String[] { "Globular Domain", "Predicted globular domain" }); @@ -122,9 +122,9 @@ public class AADisorderClient extends JabawsCalcWorker new String[] { "Protein Disorder", "Probable unstructured peptide region" }); Map> amap; - annotMap = new HashMap>>(); + annotMap = new HashMap<>(); annotMap.put(compbio.ws.client.Services.GlobPlotWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("Dydx", new HashMap()); amap.get("Dydx").put(DONTCOMBINE, DONTCOMBINE); amap.get("Dydx").put(THRESHOLD, new double[] { 1, 0 }); @@ -135,7 +135,7 @@ public class AADisorderClient extends JabawsCalcWorker amap.put("RawScore", new HashMap()); amap.get("RawScore").put(INVISIBLE, INVISIBLE); annotMap.put(compbio.ws.client.Services.DisemblWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("COILS", new HashMap()); amap.put("HOTLOOPS", new HashMap()); amap.put("REM465", new HashMap()); @@ -148,7 +148,7 @@ public class AADisorderClient extends JabawsCalcWorker amap.get("REM465").put(RANGE, new float[] { 0, 1 }); annotMap.put(compbio.ws.client.Services.IUPredWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("Long", new HashMap()); amap.put("Short", new HashMap()); amap.get("Long").put(THRESHOLD, new double[] { 1, 0.5 }); @@ -156,7 +156,7 @@ public class AADisorderClient extends JabawsCalcWorker amap.get("Short").put(THRESHOLD, new double[] { 1, 0.5 }); amap.get("Short").put(RANGE, new float[] { 0, 1 }); annotMap.put(compbio.ws.client.Services.JronnWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("JRonn", new HashMap()); amap.get("JRonn").put(THRESHOLD, new double[] { 1, 0.5 }); amap.get("JRonn").put(RANGE, new float[] { 0, 1 }); @@ -173,8 +173,8 @@ public class AADisorderClient extends JabawsCalcWorker Map> annotTypeMap = annotMap .get(service.serviceType); boolean dispFeatures = false; - Map fc = new Hashtable(); - List ourAnnot = new ArrayList(); + Map fc = new Hashtable<>(); + List ourAnnot = new ArrayList<>(); /** * grouping for any annotation rows created */ @@ -358,7 +358,6 @@ public class AADisorderClient extends JabawsCalcWorker // only do this if the alignFrame is currently showing this view. af.setShowSeqFeatures(true); } - ap.paintAlignment(true); } if (ourAnnot.size() > 0) { @@ -366,6 +365,7 @@ public class AADisorderClient extends JabawsCalcWorker // new alignment annotation rows created. updateOurAnnots(ourAnnot); ap.adjustAnnotationHeight(); + ap.paintAlignment(true, true); } } } diff --git a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java index 26fe0a2..dd64e77 100644 --- a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java +++ b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java @@ -30,6 +30,7 @@ import jalview.datamodel.AnnotatedCollectionI; import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; import jalview.gui.IProgressIndicator; +import jalview.gui.IProgressIndicatorHandler; import jalview.schemes.ResidueProperties; import jalview.workers.AlignCalcWorker; import jalview.ws.jws2.dm.AAConSettings; @@ -220,7 +221,26 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker progressId = System.currentTimeMillis()); } rslt = submitToService(seqs); + if (guiProgress != null) + { + guiProgress.registerHandler(progressId, + new IProgressIndicatorHandler() + { + @Override + public boolean cancelActivity(long id) + { + cancelCurrentJob(); + return true; + } + + @Override + public boolean canCancel() + { + return true; + } + }); + } boolean finished = false; long rpos = 0; do @@ -372,7 +392,8 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker { guiProgress.setProgressBar("", progressId); } - ap.paintAlignment(true); + // TODO: may not need to paintAlignment again ! + ap.paintAlignment(false, false); } if (msg.length() > 0) { @@ -582,7 +603,7 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker protected boolean checkDone() { calcMan.notifyStart(this); - ap.paintAlignment(false); + ap.paintAlignment(false, false); while (!calcMan.notifyWorking(this)) { if (calcMan.isWorking(this)) @@ -593,7 +614,7 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker { if (ap != null) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); } Thread.sleep(200); diff --git a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java index cb8f75a..2f3c298 100644 --- a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java +++ b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java @@ -170,13 +170,11 @@ public class Jws2Instance { try { - Closeable svc = (Closeable) service; - service = null; - svc.close(); - } catch (Exception e) + ((Closeable) service).close(); + } catch (Throwable t) { + // ignore } - ; } super.finalize(); } diff --git a/test/jalview/analysis/AlignmentGenerator.java b/test/jalview/analysis/AlignmentGenerator.java index 3187fd9..9d3877c 100644 --- a/test/jalview/analysis/AlignmentGenerator.java +++ b/test/jalview/analysis/AlignmentGenerator.java @@ -27,39 +27,25 @@ import jalview.datamodel.SequenceI; import jalview.gui.JvOptionPane; import jalview.io.FastaFile; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; import java.util.Arrays; import java.util.Random; import org.testng.annotations.BeforeClass; /** - * Generates, and outputs in Fasta format, a random DNA alignment for given + * Generates, and outputs in Fasta format, a random peptide or nucleotide alignment for given * sequence length and count. Will regenerate the same alignment each time if * the same random seed is used (so may be used for reproducible unit tests). * Not guaranteed to reproduce the same results between versions, as the rules * may get tweaked to produce more 'realistic' results. * - * Arguments: - *
      - *
    • length (number of bases in each sequence)
    • - *
    • height (number of sequences)
    • - *
    • a whole number random seed
    • - *
    • percentage of gaps to include (0-100)
    • - *
    • percentage chance of variation of each position (0-100)
    • - *
    - * * @author gmcarstairs - * */ public class AlignmentGenerator { - @BeforeClass(alwaysRun = true) - public void setUpJvOptionPane() - { - JvOptionPane.setInteractiveMode(false); - JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); - } - private static final char GAP = '-'; private static final char ZERO = '0'; @@ -72,51 +58,76 @@ public class AlignmentGenerator private Random random; + private PrintStream ps; /** - * Outputs a DNA 'alignment' where each position is a random choice from - * 'GTCA-'. + * Outputs a pseudo-randomly generated nucleotide or peptide alignment + * Arguments: + *
      + *
    • n (for nucleotide) or p (for peptide)
    • + *
    • length (number of bases in each sequence)
    • + *
    • height (number of sequences)
    • + *
    • a whole number random seed
    • + *
    • percentage of gaps to include (0-100)
    • + *
    • percentage chance of variation of each position (0-100)
    • + *
    • (optional) path to a file to write the alignment to
    • + *
    + * * * @param args + * @throws FileNotFoundException */ - public static void main(String[] args) + public static void main(String[] args) throws FileNotFoundException { - if (args.length != 6) + if (args.length != 6 && args.length != 7) { usage(); return; } + + PrintStream ps = System.out; + if (args.length == 7) + { + ps = new PrintStream(new File(args[6])); + } + boolean nucleotide = args[0].toLowerCase().startsWith("n"); int width = Integer.parseInt(args[1]); int height = Integer.parseInt(args[2]); long randomSeed = Long.valueOf(args[3]); int gapPercentage = Integer.valueOf(args[4]); int changePercentage = Integer.valueOf(args[5]); - AlignmentI al = new AlignmentGenerator(nucleotide).generate(width, - height, - randomSeed, gapPercentage, changePercentage); - System.out.println("; " + height + " sequences of " + width + ps.println("; " + height + " sequences of " + width + " bases with " + gapPercentage + "% gaps and " + changePercentage + "% mutations (random seed = " + randomSeed + ")"); - System.out.println(new FastaFile().print(al.getSequencesArray(), true)); + + new AlignmentGenerator(nucleotide, ps).generate(width, height, + randomSeed, gapPercentage, changePercentage); + + if (ps != System.out) + { + ps.close(); + } } /** - * Print parameter help. + * Prints parameter help */ private static void usage() { System.out.println("Usage:"); System.out.println("arg0: n (for nucleotide) or p (for peptide)"); System.out.println("arg1: number of (non-gap) bases per sequence"); - System.out.println("arg2: number sequences"); + System.out.println("arg2: number of sequences"); System.out .println("arg3: an integer as random seed (same seed = same results)"); System.out.println("arg4: percentage of gaps to (randomly) generate"); System.out .println("arg5: percentage of 'mutations' to (randomly) generate"); + System.out + .println("arg6: (optional) path to output file (default is sysout)"); System.out.println("Example: AlignmentGenerator n 12 15 387 10 5"); System.out .println("- 15 nucleotide sequences of 12 bases each, approx 10% gaps and 5% mutations, random seed = 387"); @@ -124,16 +135,28 @@ public class AlignmentGenerator } /** - * Constructor that sets nucleotide or peptide symbol set + * Constructor that sets nucleotide or peptide symbol set, and also writes the + * generated alignment to sysout */ public AlignmentGenerator(boolean nuc) { - BASES = nuc ? NUCS : PEPS; + this(nuc, System.out); + } + + /** + * Constructor that sets nucleotide or peptide symbol set, and also writes the + * generated alignment to the specified output stream (if not null). This can + * be used to write the alignment to a file or sysout. + */ + public AlignmentGenerator(boolean nucleotide, PrintStream printStream) + { + BASES = nucleotide ? NUCS : PEPS; + ps = printStream; } /** - * Outputs a DNA 'alignment' of given width and height, where each position is - * a random choice from 'GTCA-'. + * Outputs an 'alignment' of given width and height, where each position is a + * random choice from the symbol alphabet, or - for gap * * @param width * @param height @@ -153,6 +176,12 @@ public class AlignmentGenerator seqno + 1, width, changePercentage); } AlignmentI al = new Alignment(seqs); + + if (ps != null) + { + ps.println(new FastaFile().print(al.getSequencesArray(), true)); + } + return al; } diff --git a/test/jalview/analysis/AlignmentUtilsTests.java b/test/jalview/analysis/AlignmentUtilsTests.java index 4439bb9..06b51e6 100644 --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@ -2533,4 +2533,71 @@ public class AlignmentUtilsTests assertEquals(s_as3, uas3.getSequenceAsString()); } + /** + * Tests for the method that maps nucleotide to protein based on CDS features + */ + @Test(groups = "Functional") + public void testMapCdsToProtein() + { + SequenceI peptide = new Sequence("pep", "KLQ"); + + /* + * Case 1: CDS 3 times length of peptide + * NB method only checks lengths match, not translation + */ + SequenceI dna = new Sequence("dna", "AACGacgtCTCCT"); + dna.createDatasetSequence(); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null)); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 13, null)); + MapList ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertEquals(3, ml.getFromRatio()); + assertEquals(1, ml.getToRatio()); + assertEquals("[[1, 3]]", + Arrays.deepToString(ml.getToRanges().toArray())); + assertEquals("[[1, 4], [9, 13]]", + Arrays.deepToString(ml.getFromRanges().toArray())); + + /* + * Case 2: CDS 3 times length of peptide + stop codon + * (note code does not currently check trailing codon is a stop codon) + */ + dna = new Sequence("dna", "AACGacgtCTCCTTGA"); + dna.createDatasetSequence(); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null)); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 16, null)); + ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertEquals(3, ml.getFromRatio()); + assertEquals(1, ml.getToRatio()); + assertEquals("[[1, 3]]", + Arrays.deepToString(ml.getToRanges().toArray())); + assertEquals("[[1, 4], [9, 13]]", + Arrays.deepToString(ml.getFromRanges().toArray())); + + /* + * Case 3: CDS not 3 times length of peptide - no mapping is made + */ + dna = new Sequence("dna", "AACGacgtCTCCTTG"); + dna.createDatasetSequence(); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 1, 4, null)); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 9, 15, null)); + ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertNull(ml); + + /* + * Case 4: incomplete start codon corresponding to X in peptide + */ + dna = new Sequence("dna", "ACGacgtCTCCTTGG"); + dna.createDatasetSequence(); + SequenceFeature sf = new SequenceFeature("CDS", "", 1, 3, null); + sf.setPhase("2"); // skip 2 positions (AC) to start of next codon (GCT) + dna.addSequenceFeature(sf); + dna.addSequenceFeature(new SequenceFeature("CDS", "", 8, 15, null)); + peptide = new Sequence("pep", "XLQ"); + ml = AlignmentUtils.mapCdsToProtein(dna, peptide); + assertEquals("[[2, 3]]", + Arrays.deepToString(ml.getToRanges().toArray())); + assertEquals("[[3, 3], [8, 12]]", + Arrays.deepToString(ml.getFromRanges().toArray())); + } + } diff --git a/test/jalview/analysis/TestAlignSeq.java b/test/jalview/analysis/TestAlignSeq.java index 70e59c5..e2e5594 100644 --- a/test/jalview/analysis/TestAlignSeq.java +++ b/test/jalview/analysis/TestAlignSeq.java @@ -64,7 +64,7 @@ public class TestAlignSeq s2 = new Sequence("Seq2", "ASDFA"); s2.setStart(5); s2.setEnd(9); - s3 = new Sequence("Seq1", "SDFAQQQSSS"); + s3 = new Sequence("Seq3", "SDFAQQQSSS"); } @@ -125,10 +125,10 @@ public class TestAlignSeq }; as.printAlignment(ps); - String expected = "Score = 320.0\nLength of alignment = 10\nSequence Seq1 : 3 - 18 (Sequence length = 14)\nSequence Seq1 : 1 - 10 (Sequence length = 10)\n\n" - + "Seq1 SDFAQQQRRR\n" - + " ||||||| \n" - + "Seq1 SDFAQQQSSS\n\n" + "Percentage ID = 70.00\n"; + String expected = "Score = 320.0\nLength of alignment = 10\nSequence Seq1/4-13 (Sequence length = 14)\nSequence Seq3/1-10 (Sequence length = 10)\n\n" + + "Seq1/4-13 SDFAQQQRRR\n" + + " ||||||| \n" + + "Seq3/1-10 SDFAQQQSSS\n\n" + "Percentage ID = 70.00\n\n"; assertEquals(expected, baos.toString()); } } diff --git a/test/jalview/datamodel/CigarArrayTest.java b/test/jalview/datamodel/CigarArrayTest.java new file mode 100644 index 0000000..7bee423 --- /dev/null +++ b/test/jalview/datamodel/CigarArrayTest.java @@ -0,0 +1,141 @@ +/* + * 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.datamodel; + +import static org.testng.Assert.assertEquals; + +import jalview.gui.JvOptionPane; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class CigarArrayTest +{ + @BeforeClass(alwaysRun = true) + public void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + + @Test(groups = "Functional") + public void testConstructor() + { + SequenceI seq1 = new Sequence("sq1", + "ASFDDABACBACBACBACBACBACBABCABCBACBABCAB"); + Sequence seq2 = new Sequence("sq2", + "TTTTTTACBCBABCABCABCABCBACBACBABCABCABCBA"); + + // construct alignment + AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 }); + + // hide columns + HiddenColumns hc = new HiddenColumns(); + hc.hideColumns(3, 6); + hc.hideColumns(16, 20); + + // select group + SequenceGroup sg1 = new SequenceGroup(); + sg1.addSequence(seq1, false); + sg1.setStartRes(2); + sg1.setEndRes(23); + + // Cigar array meanings: + // M = match + // D = deletion + // I = insertion + // number preceding M/D/I is the number of residues which + // match/are deleted/are inserted + // In the CigarArray constructor only matches or deletions are created, as + // we are comparing a sequence to its own subsequence (the group) + hidden + // columns. + + // no hidden columns case + CigarArray cig = new CigarArray(al, null, sg1); + String result = cig.getCigarstring(); + assertEquals(result, "22M"); + + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "1M4D9M5D3M"); + + // group starts at hidden cols + sg1.setStartRes(3); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "4D9M5D3M"); + + // group starts at last but 1 hidden col + sg1.setStartRes(5); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D3M"); + + // group starts at last hidden col + sg1.setStartRes(6); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "1D9M5D3M"); + + // group starts just after hidden region + sg1.setStartRes(7); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "9M5D3M"); + + // group ends just before start of hidden region + sg1.setStartRes(5); + sg1.setEndRes(15); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M"); + + // group ends at start of hidden region + sg1.setEndRes(16); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M1D"); + + // group ends 1 after start of hidden region + sg1.setEndRes(17); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M2D"); + + // group ends at end of hidden region + sg1.setEndRes(20); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D"); + + // group ends just after end of hidden region + sg1.setEndRes(21); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D1M"); + + // group ends 2 after end of hidden region + sg1.setEndRes(22); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D2M"); + } +} diff --git a/test/jalview/datamodel/SequenceTest.java b/test/jalview/datamodel/SequenceTest.java index 6844072..c0cb09c 100644 --- a/test/jalview/datamodel/SequenceTest.java +++ b/test/jalview/datamodel/SequenceTest.java @@ -1672,4 +1672,135 @@ public class SequenceTest assertEquals(new Range(8, 13), sq.findPositions(1, 13)); // the lot assertEquals(new Range(8, 13), sq.findPositions(1, 99)); } + + @Test(groups = { "Functional" }) + public void testFindFeatures_largeEndPos() + { + /* + * imitate a PDB sequence where end is larger than end position + */ + SequenceI sq = new Sequence("test", "-ABC--DEF--", 1, 20); + sq.createDatasetSequence(); + + assertTrue(sq.findFeatures(1, 9).isEmpty()); + // should be no array bounds exception - JAL-2772 + assertTrue(sq.findFeatures(1, 15).isEmpty()); + + // add feature on BCD + SequenceFeature sfBCD = new SequenceFeature("Cath", "desc", 2, 4, 2f, + null); + sq.addSequenceFeature(sfBCD); + + // no features in columns 1-2 (-A) + List found = sq.findFeatures(1, 2); + assertTrue(found.isEmpty()); + + // columns 1-6 (-ABC--) includes BCD + found = sq.findFeatures(1, 6); + assertEquals(1, found.size()); + assertTrue(found.contains(sfBCD)); + + // columns 10-11 (--) should find nothing + found = sq.findFeatures(10, 11); + assertEquals(0, found.size()); + } + + @Test(groups = { "Functional" }) + public void testSetName() + { + SequenceI sq = new Sequence("test", "-ABC---DE-F--"); + assertEquals("test", sq.getName()); + assertEquals(1, sq.getStart()); + assertEquals(6, sq.getEnd()); + + sq.setName("testing"); + assertEquals("testing", sq.getName()); + + sq.setName("test/8-10"); + assertEquals("test", sq.getName()); + assertEquals(8, sq.getStart()); + assertEquals(13, sq.getEnd()); // note end is recomputed + + sq.setName("testing/7-99"); + assertEquals("testing", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); // end may be beyond physical end + + sq.setName("/2-3"); + assertEquals("", sq.getName()); + assertEquals(2, sq.getStart()); + assertEquals(7, sq.getEnd()); + + sq.setName("test/"); // invalid + assertEquals("test/", sq.getName()); + assertEquals(2, sq.getStart()); + assertEquals(7, sq.getEnd()); + + sq.setName("test/6-13/7-99"); + assertEquals("test/6-13", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/0-5"); // 0 is invalid - ignored + assertEquals("test/0-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/a-5"); // a is invalid - ignored + assertEquals("test/a-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/6-5"); // start > end is invalid - ignored + assertEquals("test/6-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/5"); // invalid - ignored + assertEquals("test/5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/-5"); // invalid - ignored + assertEquals("test/-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/5-"); // invalid - ignored + assertEquals("test/5-", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/5-6-7"); // invalid - ignored + assertEquals("test/5-6-7", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName(null); // invalid, gets converted to space + assertEquals("", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + } + + @Test(groups = { "Functional" }) + public void testCheckValidRange() + { + Sequence sq = new Sequence("test/7-12", "-ABC---DE-F--"); + assertEquals(7, sq.getStart()); + assertEquals(12, sq.getEnd()); + + /* + * checkValidRange ensures end is at least the last residue position + */ + PA.setValue(sq, "end", 2); + sq.checkValidRange(); + assertEquals(12, sq.getEnd()); + + /* + * end may be beyond the last residue position + */ + PA.setValue(sq, "end", 22); + sq.checkValidRange(); + assertEquals(22, sq.getEnd()); + } } diff --git a/test/jalview/ext/ensembl/EnsemblGeneTest.java b/test/jalview/ext/ensembl/EnsemblGeneTest.java index a8c491c..5920b89 100644 --- a/test/jalview/ext/ensembl/EnsemblGeneTest.java +++ b/test/jalview/ext/ensembl/EnsemblGeneTest.java @@ -296,4 +296,28 @@ public class EnsemblGeneTest assertEquals(-1, fc.compare("coding_exon", "feature_variant")); assertEquals(1f, fc.getTransparency()); } + + @Test(groups = "Network") + public void testGetGeneIds() + { + /* + * ENSG00000158828 gene id PINK1 human + * ENST00000321556 transcript for the same gene - should not be duplicated + * P30419 Uniprot identifier for ENSG00000136448 + * ENST00000592782 transcript for Uniprot gene - should not be duplicated + * BRAF - gene name resolvabe (at time of writing) for 6 model species + */ + String ids = "ENSG00000158828 ENST00000321556 P30419 ENST00000592782 BRAF"; + EnsemblGene testee = new EnsemblGene(); + List geneIds = testee.getGeneIds(ids); + assertEquals(8, geneIds.size()); + assertTrue(geneIds.contains("ENSG00000158828")); + assertTrue(geneIds.contains("ENSG00000136448")); + assertTrue(geneIds.contains("ENSG00000157764")); // BRAF human + assertTrue(geneIds.contains("ENSMUSG00000002413")); // mouse + assertTrue(geneIds.contains("ENSRNOG00000010957")); // rat + assertTrue(geneIds.contains("ENSXETG00000004845")); // xenopus + assertTrue(geneIds.contains("ENSDARG00000017661")); // zebrafish + assertTrue(geneIds.contains("ENSGALG00000012865")); // chicken + } } diff --git a/test/jalview/ext/ensembl/EnsemblRestClientTest.java b/test/jalview/ext/ensembl/EnsemblRestClientTest.java index 31001da..cc3a3db 100644 --- a/test/jalview/ext/ensembl/EnsemblRestClientTest.java +++ b/test/jalview/ext/ensembl/EnsemblRestClientTest.java @@ -20,21 +20,40 @@ */ package jalview.ext.ensembl; +import static org.testng.Assert.assertTrue; + import jalview.datamodel.AlignmentI; import java.net.MalformedURLException; import java.net.URL; import java.util.List; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class EnsemblRestClientTest { + private EnsemblRestClient sf; @Test(suiteName = "live") public void testIsEnsemblAvailable() { - EnsemblRestClient sf = new EnsemblRestClient() + boolean isAvailable = sf.isEnsemblAvailable(); + if (isAvailable) + { + System.out.println("Ensembl is UP!"); + } + else + { + System.err + .println("Ensembl is DOWN or unreachable ******************* BAD!"); + } + } + + @BeforeMethod(alwaysRun = true) + protected void setUp() + { + sf = new EnsemblRestClient() { @Override @@ -74,16 +93,14 @@ public class EnsemblRestClientTest } }; - boolean isAvailable = sf.isEnsemblAvailable(); - if (isAvailable) - { - System.out.println("Ensembl is UP!"); - } - else + } + + @Test(groups = "Network") + public void testCheckEnsembl_overload() + { + for (int i = 0; i < 20; i++) { - System.err - .println("Ensembl is DOWN or unreachable ******************* BAD!"); + assertTrue(sf.checkEnsembl(), "Error on " + (i + 1) + "th ping"); } } - } diff --git a/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java b/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java index aa2c315..e2af26b 100644 --- a/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java +++ b/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java @@ -191,34 +191,6 @@ public class EnsemblSeqProxyTest } - @Test(groups = "Functional") - public void testIsTranscriptIdentifier() - { - EnsemblSeqProxy testee = new EnsemblGene(); - assertFalse(testee.isTranscriptIdentifier(null)); - assertFalse(testee.isTranscriptIdentifier("")); - assertFalse(testee.isTranscriptIdentifier("ENSG00000012345")); - assertTrue(testee.isTranscriptIdentifier("ENST00000012345")); - assertTrue(testee.isTranscriptIdentifier("ENSMUST00000012345")); - assertFalse(testee.isTranscriptIdentifier("enst00000012345")); - assertFalse(testee.isTranscriptIdentifier("ENST000000123456")); - assertFalse(testee.isTranscriptIdentifier("ENST0000001234")); - } - - @Test(groups = "Functional") - public void testIsGeneIdentifier() - { - EnsemblSeqProxy testee = new EnsemblGene(); - assertFalse(testee.isGeneIdentifier(null)); - assertFalse(testee.isGeneIdentifier("")); - assertFalse(testee.isGeneIdentifier("ENST00000012345")); - assertTrue(testee.isGeneIdentifier("ENSG00000012345")); - assertTrue(testee.isGeneIdentifier("ENSMUSG00000012345")); - assertFalse(testee.isGeneIdentifier("ensg00000012345")); - assertFalse(testee.isGeneIdentifier("ENSG000000123456")); - assertFalse(testee.isGeneIdentifier("ENSG0000001234")); - } - /** * Test the method that appends a single allele's reverse complement to a * string buffer diff --git a/test/jalview/ext/ensembl/SpeciesTest.java b/test/jalview/ext/ensembl/SpeciesTest.java new file mode 100644 index 0000000..44658e7 --- /dev/null +++ b/test/jalview/ext/ensembl/SpeciesTest.java @@ -0,0 +1,30 @@ +package jalview.ext.ensembl; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.testng.annotations.Test; + +public class SpeciesTest +{ + @Test + public void testGetModelOrganisms() + { + Set models = Species.getModelOrganisms(); + assertTrue(models.contains(Species.human)); + assertFalse(models.contains(Species.horse)); + for (Species s : Species.values()) + { + if (s.isModelOrganism()) + { + assertTrue(models.contains(s)); + } + else + { + assertFalse(models.contains(s)); + } + } + } +} diff --git a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java index c9e1cad..63d5e4e 100644 --- a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java +++ b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java @@ -29,6 +29,11 @@ public class AtomSpecModelTest assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); model.addRange(0, 3, 10, "C"); // subsumes 5-9 assertEquals(model.getAtomSpec(), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B"); + model.addRange(5, 25, 35, " "); // empty chain code - e.g. from homology + // modelling + assertEquals(model.getAtomSpec(), + "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35."); + } } diff --git a/test/jalview/gui/AlignViewportTest.java b/test/jalview/gui/AlignViewportTest.java index 812fd8f..5ed0cac 100644 --- a/test/jalview/gui/AlignViewportTest.java +++ b/test/jalview/gui/AlignViewportTest.java @@ -96,57 +96,6 @@ public class AlignViewportTest testee = new AlignViewport(al); } - @Test(groups = { "Functional" }) - public void testCollateForPdb() - { - // JBP: What behaviour is this supposed to test ? - /* - * Set up sequence pdb ids - */ - PDBEntry pdb1 = new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb"); - PDBEntry pdb2 = new PDBEntry("2ABC", "C", Type.PDB, "2ABC.pdb"); - PDBEntry pdb3 = new PDBEntry("3ABC", "D", Type.PDB, "3ABC.pdb"); - - /* - * seq1 and seq3 refer to 1abcB, seq2 to 2abcC, none to 3abcD - */ - al.getSequenceAt(0).getDatasetSequence() - .addPDBId(new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb")); - al.getSequenceAt(2).getDatasetSequence() - .addPDBId(new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb")); - al.getSequenceAt(1).getDatasetSequence() - .addPDBId(new PDBEntry("2ABC", "C", Type.PDB, "2ABC.pdb")); - /* - * Add a second chain PDB xref to Seq2 - should not result in a duplicate in - * the results - */ - al.getSequenceAt(1).getDatasetSequence() - .addPDBId(new PDBEntry("2ABC", "D", Type.PDB, "2ABC.pdb")); - /* - * Seq3 refers to 3abc - this does not match 3ABC (as the code stands) - */ - al.getSequenceAt(2).getDatasetSequence() - .addPDBId(new PDBEntry("3abc", "D", Type.PDB, "3ABC.pdb")); - - /* - * run method under test - */ - SequenceI[][] seqs = testee.collateForPDB(new PDBEntry[] { pdb1, pdb2, - pdb3 }); - - // seq1 and seq3 refer to PDBEntry[0] - assertEquals(2, seqs[0].length); - assertSame(al.getSequenceAt(0), seqs[0][0]); - assertSame(al.getSequenceAt(2), seqs[0][1]); - - // seq2 refers to PDBEntry[1] - assertEquals(1, seqs[1].length); - assertSame(al.getSequenceAt(1), seqs[1][0]); - - // no sequence refers to PDBEntry[2] - assertEquals(0, seqs[2].length); - } - /** * Test that a mapping is not deregistered when a second view is closed but * the first still holds a reference to the mapping diff --git a/test/jalview/gui/AlignmentPanelTest.java b/test/jalview/gui/AlignmentPanelTest.java index b228ba1..2819dbf 100644 --- a/test/jalview/gui/AlignmentPanelTest.java +++ b/test/jalview/gui/AlignmentPanelTest.java @@ -21,6 +21,7 @@ package jalview.gui; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; import jalview.bin.Cache; import jalview.bin.Jalview; @@ -218,4 +219,31 @@ public class AlignmentPanelTest .getAlignment().getWidth() - 1 - 21); // 21 is the number of hidden // columns } + + /** + * Test that update layout reverts to original (unwrapped) values for endRes + * and endSeq when switching from wrapped to unwrapped mode (JAL-2739) + */ + @Test(groups = "Functional") + public void TestUpdateLayout_endRes() + { + // get details of original alignment dimensions + ViewportRanges ranges = af.getViewport().getRanges(); + int endres = ranges.getEndRes(); + + // wrap + af.alignPanel.getAlignViewport().setWrapAlignment(true); + af.alignPanel.updateLayout(); + + // endRes changes + assertNotEquals(ranges.getEndRes(), endres); + + // unwrap + af.alignPanel.getAlignViewport().setWrapAlignment(false); + af.alignPanel.updateLayout(); + + // endRes and endSeq back to original values + assertEquals(ranges.getEndRes(), endres); + + } } diff --git a/test/jalview/gui/FreeUpMemoryTest.java b/test/jalview/gui/FreeUpMemoryTest.java new file mode 100644 index 0000000..e93bfac --- /dev/null +++ b/test/jalview/gui/FreeUpMemoryTest.java @@ -0,0 +1,216 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import jalview.analysis.AlignmentGenerator; +import jalview.bin.Cache; +import jalview.bin.Jalview; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceGroup; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class FreeUpMemoryTest +{ + private static final int ONE_MB = 1000 * 1000; + + /** + * Configure (read-only) Jalview property settings for test + */ + @BeforeClass(alwaysRun = true) + public void setUp() + { + Jalview.main(new String[] { "-nonews", "-props", + "test/jalview/testProps.jvprops" }); + Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_QUALITY", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_CONSERVATION", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("SHOW_IDENTITY", + Boolean.TRUE.toString()); + } + + /** + * A simple test that memory is released when all windows are closed. + *
      + *
    • generates a reasonably large alignment and loads it
    • + *
    • performs various operations on the alignment
    • + *
    • closes all windows
    • + *
    • requests garbage collection
    • + *
    • asserts that the remaining memory footprint (heap usage) is 'not large' + *
    • + *
    + * If the test fails, this suggests that a reference to some large object + * (perhaps the alignment data, or some annotation / Tree / PCA data) has + * failed to be garbage collected. If this is the case, the heap will need to + * be inspected manually (suggest using jvisualvm) in order to track down + * where large objects are still referenced. The code (for example + * AlignmentViewport.dispose()) should then be updated to ensure references to + * large objects are set to null when they are no longer required. + * + * @throws IOException + */ + @Test(groups = "Memory") + public void testFreeMemoryOnClose() throws IOException + { + File f = generateAlignment(); + f.deleteOnExit(); + + doStuffInJalview(f); + + Desktop.instance.closeAll_actionPerformed(null); + + checkUsedMemory(35L); + } + + /** + * Requests garbage collection and then checks whether remaining memory in use + * is less than the expected value (in Megabytes) + * + * @param expectedMax + */ + protected void checkUsedMemory(long expectedMax) + { + /* + * request garbage collection and wait briefly for it to run; + * NB there is no guarantee when, or whether, it will do so + */ + System.gc(); + waitFor(100); + + /* + * a second gc() call should not be necessary - but it is! + * the test passes with it, and fails without it + */ + System.gc(); + waitFor(100); + + /* + * check used memory is 'reasonably low' + */ + long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB; + long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB; + long usedMemory = availableMemory - freeMemory; + + /* + * sanity check - fails if any frame was added after + * closeAll_actionPerformed + */ + assertEquals(Desktop.instance.getAllFrames().length, 0); + + /* + * if this assertion fails + * - set a breakpoint here + * - run jvisualvm to inspect a heap dump of Jalview + * - identify large objects in the heap and their referers + * - fix code as necessary to null the references on close + */ + System.out.println("Used memory after gc = " + usedMemory + "MB"); + assertTrue(usedMemory < expectedMax, String.format( + "Used memory %d should be less than %d (Recommend running test manually to verify)", + usedMemory, + expectedMax)); + } + + /** + * Loads an alignment from file and exercises various operations in Jalview + * + * @param f + */ + protected void doStuffInJalview(File f) + { + /* + * load alignment, wait for consensus and other threads to complete + */ + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(), + DataSourceType.FILE); + while (af.getViewport().isCalcInProgress()) + { + waitFor(200); + } + + /* + * set a selection group - potential memory leak if it retains + * a reference to the alignment + */ + SequenceGroup sg = new SequenceGroup(); + sg.setStartRes(0); + sg.setEndRes(100); + AlignmentI al = af.viewport.getAlignment(); + for (int i = 0; i < al.getHeight(); i++) + { + sg.addSequence(al.getSequenceAt(i), false); + } + af.viewport.setSelectionGroup(sg); + + /* + * compute Tree and PCA (on all sequences, 100 columns) + */ + af.openTreePcaDialog(); + CalculationChooser dialog = af.alignPanel.getCalculationDialog(); + dialog.openPcaPanel("BLOSUM62", dialog.getSimilarityParameters(true)); + dialog.openTreePanel("BLOSUM62", dialog.getSimilarityParameters(false)); + + /* + * wait until Tree and PCA have been computed + */ + while (af.viewport.getCurrentTree() == null + && dialog.getPcaPanel().isWorking()) + { + waitFor(10); + } + + /* + * give Swing time to add the PCA panel (?!?) + */ + waitFor(100); + } + + /** + * Wait for waitMs miliseconds + * + * @param waitMs + */ + protected void waitFor(int waitMs) + { + try + { + Thread.sleep(waitMs); + } catch (InterruptedException e) + { + } + } + + /** + * Generates an alignment and saves it in a temporary file, to be loaded by + * Jalview. We use a peptide alignment (so Conservation and Quality are + * calculated), which is wide enough to ensure Consensus, Conservation and + * Occupancy have a significant memory footprint (if not removed from the + * heap). + * + * @return + * @throws IOException + */ + private File generateAlignment() throws IOException + { + File f = File.createTempFile("MemoryTest", "fa"); + PrintStream ps = new PrintStream(f); + AlignmentGenerator ag = new AlignmentGenerator(false, ps); + int width = 100000; + int height = 100; + ag.generate(width, height, 0, 10, 15); + return f; + } +} diff --git a/test/jalview/gui/PairwiseAlignmentPanelTest.java b/test/jalview/gui/PairwiseAlignmentPanelTest.java new file mode 100644 index 0000000..3322ee8 --- /dev/null +++ b/test/jalview/gui/PairwiseAlignmentPanelTest.java @@ -0,0 +1,73 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; + +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceGroup; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; + +import javax.swing.JTextArea; + +import junit.extensions.PA; + +import org.testng.annotations.Test; + +public class PairwiseAlignmentPanelTest +{ + @Test(groups = "Functional") + public void testConstructor_withSelectionGroup() + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/uniref50.fa", DataSourceType.FILE); + AlignViewport viewport = af.getViewport(); + AlignmentI al = viewport.getAlignment(); + + /* + * select columns 29-36 of sequences 4 and 5 for alignment + * Q93XJ9_SOLTU/23-29 L-KAISNV + * FER1_PEA/26-32 V-TTTKAF + */ + SequenceGroup sg = new SequenceGroup(); + sg.addSequence(al.getSequenceAt(3), false); + sg.addSequence(al.getSequenceAt(4), false); + sg.setStartRes(28); + sg.setEndRes(35); + viewport.setSelectionGroup(sg); + + PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport); + + String text = ((JTextArea) PA.getValue(testee, "textarea")).getText(); + String expected = "Score = 80.0\n" + "Length of alignment = 4\n" + + "Sequence FER1_PEA/29-32 (Sequence length = 7)\n" + + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n" + + " FER1_PEA/29-32 TKAF\n" + " ||.\n" + + "Q93XJ9_SOLTU/23-26 LKAI\n\n" + "Percentage ID = 50.00\n\n"; + assertEquals(text, expected); + } + + /** + * This test aligns the same sequences as testConstructor_withSelectionGroup + * but as a complete alignment (no selection). Note that in fact the user is + * currently required to make a selection in order to calculate pairwise + * alignments, so this case does not arise. + */ + @Test(groups = "Functional") + public void testConstructor_noSelectionGroup() + { + String seqs = ">Q93XJ9_SOLTU/23-29\nL-KAISNV\n>FER1_PEA/26-32\nV-TTTKAF\n"; + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqs, + DataSourceType.PASTE); + AlignViewport viewport = af.getViewport(); + + PairwiseAlignPanel testee = new PairwiseAlignPanel(viewport); + + String text = ((JTextArea) PA.getValue(testee, "textarea")).getText(); + String expected = "Score = 80.0\n" + "Length of alignment = 4\n" + + "Sequence FER1_PEA/29-32 (Sequence length = 7)\n" + + "Sequence Q93XJ9_SOLTU/23-26 (Sequence length = 7)\n\n" + + " FER1_PEA/29-32 TKAF\n" + " ||.\n" + + "Q93XJ9_SOLTU/23-26 LKAI\n\n" + "Percentage ID = 50.00\n\n"; + assertEquals(text, expected); + } +} diff --git a/test/jalview/gui/ProgressBarTest.java b/test/jalview/gui/ProgressBarTest.java index a1715e9..72a288b 100644 --- a/test/jalview/gui/ProgressBarTest.java +++ b/test/jalview/gui/ProgressBarTest.java @@ -29,6 +29,7 @@ import java.awt.GridLayout; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import org.testng.Assert; import org.testng.annotations.BeforeClass; @@ -119,8 +120,15 @@ public class ProgressBarTest * @param layout * @param msgs */ - private void verifyProgress(GridLayout layout, String[] msgs) + private void verifyProgress(final GridLayout layout, final String[] msgs) { + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run() + { int msgCount = msgs.length; assertEquals(1 + msgCount, layout.getRows()); assertEquals(msgCount, statusPanel.getComponentCount()); @@ -132,5 +140,13 @@ public class ProgressBarTest assertEquals(msgs[i++], ((JLabel) ((JPanel) c).getComponent(0)).getText()); } + } + }); + } catch (Exception e) + { + throw new AssertionError( + "Unexpected exception waiting for progress bar validation", + e); + } } } diff --git a/test/jalview/gui/SeqCanvasTest.java b/test/jalview/gui/SeqCanvasTest.java new file mode 100644 index 0000000..a27bc3f --- /dev/null +++ b/test/jalview/gui/SeqCanvasTest.java @@ -0,0 +1,283 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; + +import jalview.datamodel.AlignmentI; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; + +import java.awt.Font; +import java.awt.FontMetrics; + +import junit.extensions.PA; + +import org.testng.annotations.Test; + +import sun.swing.SwingUtilities2; + +public class SeqCanvasTest +{ + /** + * Test the method that computes wrapped width in residues, height of wrapped + * widths in pixels, and the number of widths visible + */ + @Test(groups = "Functional") + public void testCalculateWrappedGeometry_noAnnotations() + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/uniref50.fa", DataSourceType.FILE); + AlignViewport av = af.getViewport(); + AlignmentI al = av.getAlignment(); + assertEquals(al.getWidth(), 157); + assertEquals(al.getHeight(), 15); + + av.setWrapAlignment(true); + av.getRanges().setStartEndSeq(0, 14); + av.setFont(new Font("SansSerif", Font.PLAIN, 14), true); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + assertEquals(charHeight, 17); + assertEquals(charWidth, 12); + + SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas; + + /* + * first with scales above, left, right + */ + av.setShowAnnotation(false); + av.setScaleAboveWrapped(true); + av.setScaleLeftWrapped(true); + av.setScaleRightWrapped(true); + FontMetrics fm = SwingUtilities2.getFontMetrics(testee, av.getFont()); + int labelWidth = fm.stringWidth("000") + charWidth; + assertEquals(labelWidth, 39); // 3 x 9 + charWidth + + /* + * width 400 pixels leaves (400 - 2*labelWidth) for residue columns + * take the whole multiple of character widths + */ + int canvasWidth = 400; + int canvasHeight = 300; + int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth; + int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(wrappedWidth, residueColumns); + assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth); + assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth); + assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"), + 2 * charHeight); + int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx"); + assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + + /* + * repeat height is 17 * (2 + 15) = 289 + * make canvas height 2 * 289 + 3 * charHeight so just enough to + * draw 2 widths and the first sequence of a third + */ + canvasHeight = charHeight * (17 * 2 + 3); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce canvas height by 1 pixel - should not be enough height + * to draw 3 widths + */ + canvasHeight -= 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * turn off scale above - can now fit in 2 and a bit widths + */ + av.setScaleAboveWrapped(false); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce height to enough for 2 widths and not quite a third + * i.e. two repeating heights + spacer + sequence - 1 pixel + */ + canvasHeight = charHeight * (16 * 2 + 2) - 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * make canvas width enough for scales and 20 residues + */ + canvasWidth = 2 * labelWidth + 20 * charWidth; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 20); + + /* + * reduce width by 1 pixel - rounds down to 19 residues + */ + canvasWidth -= 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 19); + + /* + * turn off West scale - adds labelWidth (39) to available for residues + * which with the 11 remainder makes 50 which is 4 more charWidths rem 2 + */ + av.setScaleLeftWrapped(false); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 23); + + /* + * add 10 pixels to width to fit in another whole residue column + */ + canvasWidth += 9; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 23); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + + /* + * turn off East scale to gain 39 more pixels (3 columns remainder 3) + */ + av.setScaleRightWrapped(false); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 27); + + /* + * add 9 pixels to width to gain a residue column + */ + canvasWidth += 8; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 27); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 28); + + /* + * now West but not East scale - lose 39 pixels or 4 columns + */ + av.setScaleLeftWrapped(true); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + + /* + * adding 3 pixels to width regains one column + */ + canvasWidth += 2; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 25); + + /* + * turn off scales left and right, make width exactly 157 columns + */ + av.setScaleLeftWrapped(false); + canvasWidth = al.getWidth() * charWidth; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + } + + /** + * Test the method that computes wrapped width in residues, height of wrapped + * widths in pixels, and the number of widths visible + */ + @Test(groups = "Functional") + public void testCalculateWrappedGeometry_withAnnotations() + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/uniref50.fa", DataSourceType.FILE); + AlignViewport av = af.getViewport(); + AlignmentI al = av.getAlignment(); + assertEquals(al.getWidth(), 157); + assertEquals(al.getHeight(), 15); + + av.setWrapAlignment(true); + av.getRanges().setStartEndSeq(0, 14); + av.setFont(new Font("SansSerif", Font.PLAIN, 14), true); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + assertEquals(charHeight, 17); + assertEquals(charWidth, 12); + + SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas; + + /* + * first with scales above, left, right + */ + av.setShowAnnotation(true); + av.setScaleAboveWrapped(true); + av.setScaleLeftWrapped(true); + av.setScaleRightWrapped(true); + FontMetrics fm = SwingUtilities2.getFontMetrics(testee, av.getFont()); + int labelWidth = fm.stringWidth("000") + charWidth; + assertEquals(labelWidth, 39); // 3 x 9 + charWidth + int annotationHeight = testee.getAnnotationHeight(); + + /* + * width 400 pixels leaves (400 - 2*labelWidth) for residue columns + * take the whole multiple of character widths + */ + int canvasWidth = 400; + int canvasHeight = 300; + int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth; + int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(wrappedWidth, residueColumns); + assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth); + assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth); + assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"), + 2 * charHeight); + int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx"); + assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()) + + annotationHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + + /* + * repeat height is 17 * (2 + 15) = 289 + annotationHeight = 507 + * make canvas height 2 * 289 + 3 * charHeight so just enough to + * draw 2 widths and the first sequence of a third + */ + canvasHeight = charHeight * (17 * 2 + 3) + 2 * annotationHeight; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce canvas height by 1 pixel - should not be enough height + * to draw 3 widths + */ + canvasHeight -= 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * turn off scale above - can now fit in 2 and a bit widths + */ + av.setScaleAboveWrapped(false); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce height to enough for 2 widths and not quite a third + * i.e. two repeating heights + spacer + sequence - 1 pixel + */ + canvasHeight = charHeight * (16 * 2 + 2) + 2 * annotationHeight - 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * add 1 pixel to height - should now get 3 widths drawn + */ + canvasHeight += 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + } +} diff --git a/test/jalview/gui/StructureViewerTest.java b/test/jalview/gui/StructureViewerTest.java index c1c1d5c..4d5b114 100644 --- a/test/jalview/gui/StructureViewerTest.java +++ b/test/jalview/gui/StructureViewerTest.java @@ -1,10 +1,17 @@ package jalview.gui; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; import jalview.datamodel.PDBEntry; import jalview.datamodel.PDBEntry.Type; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; + +import java.util.Map; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -20,9 +27,11 @@ public class StructureViewerTest } @Test(groups = "Functional") - public void testGetUniquePdbFiles() + public void testGetSequencesForPdbs() { - assertNull(StructureViewer.getUniquePdbFiles(null)); + StructureViewer sv = new StructureViewer(null); + + assertNull(sv.getSequencesForPdbs(null, null)); PDBEntry pdbe1 = new PDBEntry("1A70", "A", Type.PDB, "path1"); PDBEntry pdbe2 = new PDBEntry("3A6S", "A", Type.PDB, "path2"); @@ -30,13 +39,45 @@ public class StructureViewerTest PDBEntry pdbe4 = new PDBEntry("1GAQ", "A", Type.PDB, null); PDBEntry pdbe5 = new PDBEntry("3A6S", "B", Type.PDB, "path2"); PDBEntry pdbe6 = new PDBEntry("1GAQ", "B", Type.PDB, null); + PDBEntry[] pdbs = new PDBEntry[] { pdbe1, pdbe2, pdbe3, pdbe4, pdbe5, + pdbe6 }; + + /* + * seq1 ... seq6 associated with pdbe1 ... pdbe6 + */ + SequenceI[] seqs = new SequenceI[pdbs.length]; + for (int i = 0; i < seqs.length; i++) + { + seqs[i] = new Sequence("Seq" + i, "abc"); + } /* - * pdbe2 and pdbe5 get removed as having a duplicate file path + * pdbe3/5/6 should get removed as having a duplicate file path */ - PDBEntry[] uniques = StructureViewer.getUniquePdbFiles(new PDBEntry[] { - pdbe1, pdbe2, pdbe3, pdbe4, pdbe5, pdbe6 }); - assertEquals(uniques, - new PDBEntry[] { pdbe1, pdbe2, pdbe4, pdbe6 }); + Map uniques = sv.getSequencesForPdbs(pdbs, seqs); + assertTrue(uniques.containsKey(pdbe1)); + assertTrue(uniques.containsKey(pdbe2)); + assertFalse(uniques.containsKey(pdbe3)); + assertTrue(uniques.containsKey(pdbe4)); + assertFalse(uniques.containsKey(pdbe5)); + assertFalse(uniques.containsKey(pdbe6)); + + // 1A70 associates with seq1 and seq3 + SequenceI[] ss = uniques.get(pdbe1); + assertEquals(ss.length, 2); + assertSame(seqs[0], ss[0]); + assertSame(seqs[2], ss[1]); + + // 3A6S has seq2 and seq5 + ss = uniques.get(pdbe2); + assertEquals(ss.length, 2); + assertSame(seqs[1], ss[0]); + assertSame(seqs[4], ss[1]); + + // 1GAQ has seq4 and seq6 + ss = uniques.get(pdbe4); + assertEquals(ss.length, 2); + assertSame(seqs[3], ss[0]); + assertSame(seqs[5], ss[1]); } } diff --git a/test/jalview/renderer/ScaleRendererTest.java b/test/jalview/renderer/ScaleRendererTest.java index cf1039f..0af67cd 100644 --- a/test/jalview/renderer/ScaleRendererTest.java +++ b/test/jalview/renderer/ScaleRendererTest.java @@ -26,11 +26,11 @@ public class ScaleRendererTest AlignViewport av = af.getViewport(); /* - * scale has minor ticks at 5 and 15, major at 10 and 20 + * scale has minor ticks at 5, 15, 25, major at 10 and 20 * (these are base 1, ScaleMark holds base 0 values) */ List marks = new ScaleRenderer().calculateMarks(av, 0, 25); - assertEquals(marks.size(), 4); + assertEquals(marks.size(), 5); assertFalse(marks.get(0).major); assertEquals(marks.get(0).column, 4); @@ -48,6 +48,10 @@ public class ScaleRendererTest assertEquals(marks.get(3).column, 19); assertEquals(marks.get(3).text, "20"); + assertFalse(marks.get(4).major); + assertEquals(marks.get(4).column, 24); + assertNull(marks.get(4).text); + /* * now hide columns 9-11 and 18-20 (base 1) * scale marks are now in the same columns as before, but @@ -56,7 +60,7 @@ public class ScaleRendererTest av.hideColumns(8, 10); av.hideColumns(17, 19); marks = new ScaleRenderer().calculateMarks(av, 0, 25); - assertEquals(marks.size(), 4); + assertEquals(marks.size(), 5); assertFalse(marks.get(0).major); assertEquals(marks.get(0).column, 4); assertNull(marks.get(0).text); @@ -69,5 +73,8 @@ public class ScaleRendererTest assertTrue(marks.get(3).major); assertEquals(marks.get(3).column, 19); assertEquals(marks.get(3).text, "26"); // +6 hidden columns + assertFalse(marks.get(4).major); + assertEquals(marks.get(4).column, 24); + assertNull(marks.get(4).text); } } diff --git a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java index 7fd7abc..f6dfed6 100644 --- a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java +++ b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java @@ -2,6 +2,7 @@ package jalview.renderer.seqfeatures; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; @@ -285,6 +286,28 @@ public class FeatureColourFinderTest } @Test(groups = "Functional") + public void testFindFeatureAtEnd() + { + /* + * terminal residue feature + */ + seq.addSequenceFeature(new SequenceFeature("PDBRESNUM", "pdb res 1", + seq.getEnd(), seq.getEnd(), Float.NaN, "1seq.pdb")); + fr.setColour("PDBRESNUM", new FeatureColour(Color.red)); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * final column should have PDBRESNUM feature, the others not + */ + Color c = finder.findFeatureColour(Color.blue, seq, + seq.getLength() - 2); + assertNotEquals(c, Color.red); + c = finder.findFeatureColour(Color.blue, seq, seq.getLength() - 1); + assertEquals(c, Color.red); + } + + @Test(groups = "Functional") public void testFindFeatureColour_graduatedFeatureColour() { seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2, diff --git a/test/jalview/structures/models/AAStructureBindingModelTest.java b/test/jalview/structures/models/AAStructureBindingModelTest.java index aea3687..af02d5e 100644 --- a/test/jalview/structures/models/AAStructureBindingModelTest.java +++ b/test/jalview/structures/models/AAStructureBindingModelTest.java @@ -275,11 +275,11 @@ public class AAStructureBindingModelTest StructureSelectionManager ssm = new StructureSelectionManager(); ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1, - DataSourceType.PASTE); + DataSourceType.PASTE, null); ssm.setMapping(new SequenceI[] { seq2 }, null, PDB_2, - DataSourceType.PASTE); + DataSourceType.PASTE, null); ssm.setMapping(new SequenceI[] { seq3 }, null, PDB_3, - DataSourceType.PASTE); + DataSourceType.PASTE, null); testee = new AAStructureBindingModel(ssm, pdbFiles, seqs, null) { diff --git a/test/jalview/util/MappingUtilsTest.java b/test/jalview/util/MappingUtilsTest.java index d0ec3e8..5226819 100644 --- a/test/jalview/util/MappingUtilsTest.java +++ b/test/jalview/util/MappingUtilsTest.java @@ -1149,4 +1149,49 @@ public class MappingUtilsTest assertEquals("[12, 11, 8, 4]", Arrays.toString(ranges)); } + @Test(groups = "Functional") + public void testRemoveEndPositions() + { + List ranges = new ArrayList<>(); + + /* + * case 1: truncate last range + */ + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 30 }); + MappingUtils.removeEndPositions(5, ranges); + assertEquals(2, ranges.size()); + assertEquals(25, ranges.get(1)[1]); + + /* + * case 2: remove last range + */ + ranges.clear(); + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 22 }); + MappingUtils.removeEndPositions(3, ranges); + assertEquals(1, ranges.size()); + assertEquals(10, ranges.get(0)[1]); + + /* + * case 3: truncate penultimate range + */ + ranges.clear(); + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 21 }); + MappingUtils.removeEndPositions(3, ranges); + assertEquals(1, ranges.size()); + assertEquals(9, ranges.get(0)[1]); + + /* + * case 4: remove last two ranges + */ + ranges.clear(); + ranges.add(new int[] { 1, 10 }); + ranges.add(new int[] { 20, 20 }); + ranges.add(new int[] { 30, 30 }); + MappingUtils.removeEndPositions(3, ranges); + assertEquals(1, ranges.size()); + assertEquals(9, ranges.get(0)[1]); + } } diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java index 851b1b7..c0cb4ba 100644 --- a/test/jalview/viewmodel/ViewportRangesTest.java +++ b/test/jalview/viewmodel/ViewportRangesTest.java @@ -763,6 +763,66 @@ public class ViewportRangesTest { } } } + + @Test(groups = { "Functional" }) + public void testScrollUp_wrapped() + { + /* + * alignment 30 tall and 45 wide + */ + AlignmentI al2 = gen.generate(45, 30, 1, 0, 5); + + /* + * wrapped view, 5 sequences high, start at sequence offset 1 + */ + ViewportRanges vr = new ViewportRanges(al2); + vr.setWrappedMode(true); + vr.setViewportStartAndHeight(1, 5); + + /* + * offset wrapped view to column 3 + */ + vr.setStartEndRes(3, 22); + + int startRes = vr.getStartRes(); + int width = vr.getViewportWidth(); + assertEquals(startRes, 3); + assertEquals(width, 20); + + // in wrapped mode, we change startRes but not startSeq + // scroll down: + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 23); + + // scroll up returns to original position + vr.scrollUp(true); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 3); + + // scroll up again returns to 'origin' + vr.scrollUp(true); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 0); + + /* + * offset 3 columns once more and do some scroll downs + */ + vr.setStartEndRes(3, 22); + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 23); + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 43); + + /* + * scroll down beyond end of alignment does nothing + */ + vr.scrollUp(false); + assertEquals(vr.getStartSeq(), 1); + assertEquals(vr.getStartRes(), 43); + } } // mock listener for property change events diff --git a/utils/checkstyle/checkstyle-suppress.xml b/utils/checkstyle/checkstyle-suppress.xml index ac9e260..122b8d0 100644 --- a/utils/checkstyle/checkstyle-suppress.xml +++ b/utils/checkstyle/checkstyle-suppress.xml @@ -21,10 +21,12 @@ - - - - + + + + + +