From: Jim Procter Date: Wed, 8 Nov 2017 17:02:42 +0000 (+0000) Subject: Merge branch 'bug/JAL-2742' into develop X-Git-Tag: Release_2_10_3b1~45 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=656c7531a92b29f19f3dcf0475ef7442b775d55a;hp=a221bd3cc47ca52cf34729eef65be81ed09f9885;p=jalview.git Merge branch 'bug/JAL-2742' into develop --- 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/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/releases.html b/help/html/releases.html index 5a54d30..510e477 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
@@ -93,17 +93,21 @@ li:before {
  • '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
  • Testing and Deployment +
    General Desktop Applet
    + BioJSON
    + +
    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/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index c83f93f..262948d 100644 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -59,15 +59,6 @@ public class AlignViewport extends AlignmentViewport private AnnotationColumnChooser annotationColumnSelectionState; - @Override - public void finalize() - { - applet = null; - quality = null; - alignment = null; - colSel = null; - } - public AlignViewport(AlignmentI al, JalviewLite applet) { super(al); diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index 906acc1..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 diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index 55320ed..9a61f5f 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -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) { diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java index 69e31cf..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) { 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/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/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index e166fc1..298688b 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -2712,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) { /* diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 90271c8..4d09084 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -583,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() { diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 2d1ba12..128481c 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -2338,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("|"); diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index b768339..3f1d9c7 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -541,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); diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java index 80ac189..f3c8e8f 100755 --- a/src/jalview/gui/FontChooser.java +++ b/src/jalview/gui/FontChooser.java @@ -235,7 +235,7 @@ public class FontChooser extends GFontChooser ap.av.setScaleProteinAsCdna(oldProteinScale); ap.av.setProteinFontAsCdna(oldMirrorFont); ap.av.antiAlias = oldSmoothFont; - ap.paintAlignment(true, false); + ap.fontChanged(); if (scaleAsCdna.isVisible() && scaleAsCdna.isEnabled()) { diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index a46c2c1..1f2a3ad 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -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); } diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index 9e319c1..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) @@ -5343,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(); @@ -5384,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 9ddb751..43b4310 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -329,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) @@ -342,7 +358,7 @@ public class OverviewPanel extends JPanel @Override public void propertyChange(PropertyChangeEvent evt) { - setBoxPosition(); + setBoxPositionOnly(); } /** 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/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 930a6a5..2223ee5 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -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; @@ -1627,7 +1630,7 @@ public class SeqPanel extends JPanel av.getRanges().scrollRight(true); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(false); } @@ -1638,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); } /** diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index 20f4a49..37632ef 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -927,16 +927,10 @@ public class StructureChooser extends GStructureChooser } 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]); - - setProgressBar(MessageManager - .getString("status.fetching_3d_structures_for_selected_entries"), progressId); - sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel); + setProgressBar(MessageManager.getString( + "status.fetching_3d_structures_for_selected_entries"), + progressId); + sViewer.viewStructures(pdbEntriesToView, sequences, alignPanel); } else { 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/TreePanel.java b/src/jalview/gui/TreePanel.java index 9403057..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 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/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/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/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 fdad0ce..a0cbff4 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -1338,7 +1338,10 @@ public abstract class AlignmentViewport public void removePropertyChangeListener( java.beans.PropertyChangeListener listener) { - changeSupport.removePropertyChangeListener(listener); + if (changeSupport != null) + { + changeSupport.removePropertyChangeListener(listener); + } } /** 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/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/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/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/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/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/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