From: gmungoc Date: Thu, 26 Oct 2017 13:32:13 +0000 (+0100) Subject: Merge branch 'develop' into features/JAL-1793VCF X-Git-Tag: Release_2_11_0~181 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=06de78be50c3934158fa1d35ec92ad86b54e959f;hp=705d1c84c9a03bb0378593e4d8d8a90c8a11f831;p=jalview.git Merge branch 'develop' into features/JAL-1793VCF Conflicts: src/jalview/util/MappingUtils.java test/jalview/analysis/AlignmentUtilsTests.java test/jalview/util/MappingUtilsTest.java --- diff --git a/help/html/releases.html b/help/html/releases.html index cb2b278..09595b0 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -89,11 +89,25 @@ li:before { 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
  • + Testing and Deployment + +
    - + General + + Desktop + Applet
    + +
    @@ -1462,6 +1489,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/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/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java index 228446b..bef667d 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -2217,7 +2217,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 @@ -2229,6 +2232,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(); @@ -2252,8 +2264,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 }); @@ -2264,7 +2280,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. @@ -2283,7 +2299,6 @@ public class AlignmentUtils return result; } SequenceFeatures.sortFeatures(sfs, true); - int startPhase = 0; for (SequenceFeature sf : sfs) { @@ -2301,7 +2316,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) @@ -2316,16 +2331,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 3cb06c1..931eba6 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -21,6 +21,7 @@ package jalview.api; import jalview.analysis.Conservation; +import jalview.analysis.TreeModel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; @@ -485,4 +486,8 @@ public interface AlignViewportI extends ViewStyleI */ @Override void setProteinFontAsCdna(boolean b); + + public abstract TreeModel getCurrentTree(); + + public abstract void setCurrentTree(TreeModel tree); } diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 676d3cf..ef87671 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -1603,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(); } @@ -4147,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..c83f93f 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,8 +53,6 @@ public class AlignViewport extends AlignmentViewport boolean validCharWidth = true; - TreeModel currentTree = null; - public jalview.bin.JalviewLite applet; boolean MAC = false; @@ -274,16 +271,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/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/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index 0256055..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; @@ -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/datamodel/SequenceFeature.java b/src/jalview/datamodel/SequenceFeature.java index f5a9b42..420ade1 100755 --- a/src/jalview/datamodel/SequenceFeature.java +++ b/src/jalview/datamodel/SequenceFeature.java @@ -62,6 +62,8 @@ public class SequenceFeature implements FeatureLocationI static { INFO_KEYS.put("CSQ", ","); + // todo capture second level metadata (CSQ FORMAT) + // and delimiter "|" so as to report in a table within a table? } /* @@ -586,23 +588,23 @@ public class SequenceFeature implements FeatureLocationI { continue; // to avoid double reporting } - sb.append("").append(key).append(""); if (INFO_KEYS.containsKey(key)) { /* * split selected INFO data by delimiter over multiple lines */ - sb.append(""); String delimiter = INFO_KEYS.get(key); String[] values = entry.getValue().toString().split(delimiter); for (String value : values) { - sb.append(" ").append(value) + sb.append("").append(key).append("") + .append(value) .append(""); } } else - { + { // tried but it failed to provide a tooltip :-( + sb.append("").append(key).append(""); sb.append(entry.getValue().toString()).append(""); } } diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 50aba62..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]; @@ -1060,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 @@ -1127,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; @@ -1159,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); } @@ -1227,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/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index bf94ec0..010d52a 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -164,8 +164,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignViewport viewport; - ViewportRanges vpRanges; - public AlignViewControllerI avc; List alignPanels = new ArrayList<>(); @@ -337,7 +335,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) @@ -655,9 +652,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(); @@ -690,10 +688,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; } } @@ -2149,7 +2147,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. @@ -2550,7 +2548,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 { @@ -2615,13 +2613,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()); @@ -2654,12 +2653,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()); diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index c22a37d..90271c8 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 @@ -1110,5 +1087,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 b2c4809..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; @@ -121,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)); @@ -153,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 { @@ -166,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); } } @@ -377,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) @@ -404,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; } @@ -440,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); } /* @@ -476,7 +475,7 @@ public class AlignmentPanel extends GAlignmentPanel implements } else { - scrollNeeded = vpRanges.scrollToWrappedVisible(start); + scrollNeeded = ranges.scrollToWrappedVisible(start); } paintAlignment(redrawOverview, 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,7 +628,7 @@ public class AlignmentPanel extends GAlignmentPanel implements { int widthInRes = getSeqPanel().seqCanvas .getWrappedCanvasWidth(canvasWidth); - vpRanges.setViewportWidth(widthInRes); + ranges.setViewportWidth(widthInRes); } else { @@ -636,8 +636,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); } } @@ -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,7 +768,7 @@ public class AlignmentPanel extends GAlignmentPanel implements { return; } - vpRanges.setViewportStartAndHeight(y, height); + ranges.setViewportStartAndHeight(y, height); } repaint(); } @@ -783,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(); @@ -792,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; @@ -803,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 @@ -826,8 +830,8 @@ 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); } }); } @@ -880,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()); } /** @@ -892,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) @@ -1604,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); @@ -1633,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; } } @@ -1882,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/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..829135b --- /dev/null +++ b/src/jalview/gui/AquaInternalFrameManager.java @@ -0,0 +1,256 @@ +/* + * 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) + * + */ +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..2d1ba12 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(); }; }); @@ -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/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 35fd1b4..a4f79c2 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 b357234..9e319c1 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -1084,7 +1084,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 +1102,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 +4250,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 diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 51d7a84..9ddb751 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; @@ -350,8 +352,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/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/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 282c810..6148a2e 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -718,10 +718,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) @@ -734,7 +736,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; @@ -1626,7 +1629,7 @@ public class SeqPanel extends JPanel av.getRanges().scrollRight(true); } - else if (!av.getWrapAlignment()) + else { av.getRanges().scrollUp(false); } @@ -1637,12 +1640,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 da10e3f..20f4a49 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,43 @@ 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(); + 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")); + + setProgressBar(MessageManager + .getString("status.fetching_3d_structures_for_selected_entries"), progressId); sViewer.viewStructures(pdbEntriesToView, collatedSeqs, 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 +1004,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 +1066,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/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index c8854a7..31c20ed 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -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 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/util/MappingUtils.java b/src/jalview/util/MappingUtils.java index d21eac3..f5dd883 100644 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@ -967,4 +967,55 @@ public final class MappingUtils return (min <= queryRange[0] && max >= queryRange[0] && min <= queryRange[1] && max >= queryRange[1]); } + + /** + * 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/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index b260cab..fdad0ce 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; @@ -79,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(); @@ -947,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); } @@ -2869,6 +2874,8 @@ public abstract class AlignmentViewport */ private SearchResultsI searchResults = null; + protected TreeModel currentTree = null; + @Override public boolean hasSearchResults() { @@ -2927,4 +2934,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/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java index b972ab8..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 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 d229a39..1bff8bf 100644 --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@ -64,6 +64,8 @@ import org.testng.annotations.Test; public class AlignmentUtilsTests { + private static Sequence ts = new Sequence("short", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"); @BeforeClass(alwaysRun = true) public void setUpJvOptionPane() @@ -72,9 +74,6 @@ public class AlignmentUtilsTests JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); } - private static Sequence ts = new Sequence("short", - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"); - @Test(groups = { "Functional" }) public void testExpandContext() { @@ -2633,4 +2632,71 @@ public class AlignmentUtilsTests assertEquals("[ [1, 12] ] 1:1 to [ [158, 164, 210, 214] ]", toMap.toString()); } + + /** + * 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/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/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/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 87070d7..d4cf98a 100644 --- a/test/jalview/util/MappingUtilsTest.java +++ b/test/jalview/util/MappingUtilsTest.java @@ -1238,4 +1238,49 @@ public class MappingUtilsTest assertFalse(MappingUtils.rangeContains(null, new int[] { 1, 10 })); } + @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