From: gmungoc Date: Tue, 24 Oct 2017 12:50:46 +0000 (+0100) Subject: JAL-2738 update spike branch with latest X-Git-Tag: Release_2_11_0~62^2~23 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=a57976ba40e1abe6d7c1940386e1a25419ef9c9d;p=jalview.git JAL-2738 update spike branch with latest --- diff --git a/.checkstyle b/.checkstyle index 0329bb7..a87fa04 100644 --- a/.checkstyle +++ b/.checkstyle @@ -8,5 +8,4 @@ - diff --git a/examples/groovy/PIDmatrix.groovy b/examples/groovy/PIDmatrix.groovy new file mode 100644 index 0000000..b97abcc --- /dev/null +++ b/examples/groovy/PIDmatrix.groovy @@ -0,0 +1,99 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ + +import jalview.analysis.scoremodels.ScoreModels +import jalview.analysis.scoremodels.SimilarityParams + +// generate matrix for current selection using standard Jalview PID + +printSimilarityMatrix(true,true,SimilarityParams.Jalview) + +/** + * this function prints a sequence similarity matrix in PHYLIP format. + * printSimilarityMatrix(selected-only, include-ids, pidMethod) + * + * Allowed values for pidMethod: + * + * Jalview's Comparison.PID method includes matching gaps + * and counts over the length of the shorter gapped sequence + * SimilarityParams.Jalview; + * + * 'SeqSpace' mode PCA calculation does not count matching + * gaps but uses longest gapped sequence length + * SimilarityParams.SeqSpace; + * + * PID calcs from the Raghava-Barton paper + * SimilarityParams.PID1: ignores gap-gap, does not score gap-residue, + * includes gap-residue in lengths, matches on longer of two sequences. + * + * SimilarityParams.PID2: ignores gap-gap,ignores gap-residue, + * matches on longer of two sequences + * + * SimilarityParams.PID3: ignores gap-gap,ignores gap-residue, + * matches on shorter of sequences only + * + * SimilarityParams.PID4: ignores gap-gap,does not score gap-residue, + * includes gap-residue in lengths,matches on shorter of sequences only. + */ + +void printSimilarityMatrix(boolean selview=false, boolean includeids=true, SimilarityParams pidMethod) { + + def currentAlignFrame = jalview.bin.Jalview.getCurrentAlignFrame() + + jalview.gui.AlignViewport av = currentAlignFrame.getCurrentView() + + jalview.datamodel.AlignmentView seqStrings = av.getAlignmentView(selview) + + if (!selview || av.getSelectionGroup()==null) { + start = 0 + end = av.getAlignment().getWidth() + seqs = av.getAlignment().getSequencesArray() + } else { + start = av.getSelectionGroup().getStartRes() + end = av.getSelectionGroup().getEndRes() + 1 + seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment()) + } + + distanceCalc = ScoreModels.getInstance().getScoreModel("PID", + (jalview.api.AlignmentViewPanel) currentAlignFrame.alignPanel) + + def distance=distanceCalc.findSimilarities( + seqStrings.getSequenceStrings(jalview.util.Comparison.GAP_DASH),pidMethod) + + // output the PHYLIP Matrix + + print distance.width()+" "+distance.height()+"\n" + + p = 0 + + for (v in 1..distance.height()) { + + if (includeids) { + print seqs[p++].getDisplayId(false)+" " + } + + for (r in 1..distance.width()) { + print distance.getValue(v-1,r-1)+" " + } + + print "\n" + } +} \ No newline at end of file diff --git a/help/html/releases.html b/help/html/releases.html index 8e55ca1..cb2b278 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -81,13 +81,29 @@ li:before { Faster and more efficient management and rendering of sequence features +
  • + More reliable Ensembl fetching with HTTP + 429 rate limit request hander +
  • +
  • + Structure views don't get updated unless + their colours have changed +
  • +
    • Example groovy script for generating a matrix of percent identity scores for current alignment.
      +
    • Structures with whitespace chainCode cannot be viewed in Chimera
    • Protein annotation panel too high in CDS/Protein view
    • +
    • Can't edit the query after the server error warning icon is shown in Uniprot and PDB Free Text Search Dialogs +
    • +
    • Slow EnsemblGenome ID lookup
    • +
    • Race condition when parsing sequence ID strings in parallel
    • +
    • Hidden column marker in last column not rendered when switching back from Wrapped to normal view
    • +
    • Annotation display corrupted when scrolling right in unwapped alignment view
    diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java index 0045e97..3abbe75 100644 --- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java +++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java @@ -383,6 +383,8 @@ public class ChimeraResidue implements ChimeraStructuralObject, public void splitInsertionCode(String residue) { // OK, split the index into number and insertion code + // JBPNote - m.matches() can be true even if there is no resnum - this can + // cause NumberFormatExceptions below Pattern p = Pattern.compile("(\\d*)([A-Z]?)"); Matcher m = p.matcher(residue); if (m.matches()) diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 9e6d1c0..3cb06c1 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -24,7 +24,6 @@ import jalview.analysis.Conservation; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; -import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; import jalview.datamodel.ProfilesI; import jalview.datamodel.SearchResultsI; @@ -243,16 +242,6 @@ public interface AlignViewportI extends ViewStyleI void clearSequenceColours(); /** - * This method returns the visible alignment as text, as seen on the GUI, ie - * if columns are hidden they will not be returned in the result. Use this for - * calculating trees, PCA, redundancy etc on views which contain hidden - * columns. - * - * @return String[] - */ - CigarArray getViewAsCigars(boolean selectedRegionOnly); - - /** * return a compact representation of the current alignment selection to pass * to an analysis function * @@ -486,6 +475,7 @@ public interface AlignViewportI extends ViewStyleI * * @return */ + @Override boolean isProteinFontAsCdna(); /** @@ -493,5 +483,6 @@ public interface AlignViewportI extends ViewStyleI * * @return */ + @Override void setProteinFontAsCdna(boolean b); } diff --git a/src/jalview/api/AlignmentViewPanel.java b/src/jalview/api/AlignmentViewPanel.java index ef59996..0b1ca21 100644 --- a/src/jalview/api/AlignmentViewPanel.java +++ b/src/jalview/api/AlignmentViewPanel.java @@ -43,9 +43,10 @@ public interface AlignmentViewPanel extends OOMHandlerI * * @param updateOverview * - if true, the overview panel will also be updated and repainted + * @param updateStructures + * - if true then any linked structure views will also be updated */ - - void paintAlignment(boolean updateOverview); + void paintAlignment(boolean updateOverview, boolean updateStructures); /** * automatically adjust annotation panel height for new annotation whilst diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index 9d44479..46bd4fd 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -383,7 +383,7 @@ public class APopupMenu extends java.awt.PopupMenu void addFeatureLinks(final SequenceI seq, List links) { Menu linkMenu = new Menu(MessageManager.getString("action.link")); - Map> linkset = new LinkedHashMap>(); + Map> linkset = new LinkedHashMap<>(); for (String link : links) { @@ -485,8 +485,8 @@ public class APopupMenu extends java.awt.PopupMenu * Temporary store to hold distinct calcId / type pairs for the tooltip. * Using TreeMap means calcIds are shown in alphabetical order. */ - SortedMap tipEntries = new TreeMap(); - final Map> candidates = new LinkedHashMap>(); + SortedMap tipEntries = new TreeMap<>(); + final Map> candidates = new LinkedHashMap<>(); AlignmentI al = this.ap.av.getAlignment(); AlignmentUtils.findAddableReferenceAnnotations(forSequences, tipEntries, candidates, al); @@ -825,8 +825,8 @@ public class APopupMenu extends java.awt.PopupMenu } int gSize = sg.getSize(); - List seqs = new ArrayList(); - List features = new ArrayList(); + List seqs = new ArrayList<>(); + List features = new ArrayList<>(); for (int i = 0; i < gSize; i++) { @@ -930,7 +930,7 @@ public class APopupMenu extends java.awt.PopupMenu { seq.setName(dialog.getName()); seq.setDescription(dialog.getDescription()); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -1163,7 +1163,7 @@ public class APopupMenu extends java.awt.PopupMenu void refresh() { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } protected void clustalColour_actionPerformed() @@ -1339,7 +1339,7 @@ public class APopupMenu extends java.awt.PopupMenu SequenceGroup sg = ap.av.getSelectionGroup(); ap.av.getAlignment().deleteGroup(sg); ap.av.setSelectionGroup(null); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } void createGroupMenuItem_actionPerformed() @@ -1440,8 +1440,8 @@ public class APopupMenu extends java.awt.PopupMenu * the insertion order, which is the order of the annotations on the * alignment. */ - Map>> shownTypes = new LinkedHashMap>>(); - Map>> hiddenTypes = new LinkedHashMap>>(); + Map>> shownTypes = new LinkedHashMap<>(); + Map>> hiddenTypes = new LinkedHashMap<>(); AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes, AlignmentAnnotationUtils.asList(annotations), forSequences); diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index ed04a0a..676d3cf 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -343,7 +343,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, createAlignFrameWindow(embedded); validate(); alignPanel.adjustAnnotationHeight(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } public AlignViewport getAlignViewport() @@ -415,7 +415,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { viewport.featureSettings.refreshTable(); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); statusBar.setText(MessageManager .getString("label.successfully_added_features_alignment")); } @@ -691,7 +691,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, break; } - alignPanel.paintAlignment(true); + // TODO: repaint flags set only if the keystroke warrants it + alignPanel.paintAlignment(true, true); } /** @@ -917,7 +918,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { applyAutoAnnotationSettings_actionPerformed(); } - alignPanel.paintAlignment(true); + // TODO: repaint flags set only if warranted + alignPanel.paintAlignment(true, true); } /** @@ -1094,7 +1096,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, else if (source == invertColSel) { viewport.invertColumnSelection(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); viewport.sendSelection(); } else if (source == remove2LeftMenuItem) @@ -1128,34 +1130,34 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, else if (source == showColumns) { viewport.showAllHiddenColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == showSeqs) { viewport.showAllHiddenSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); // uncomment if we want to slave sequence selections in split frame // viewport.sendSelection(); } else if (source == hideColumns) { viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == hideSequences && viewport.getSelectionGroup() != null) { viewport.hideAllSelectedSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); // uncomment if we want to slave sequence selections in split frame // viewport.sendSelection(); } else if (source == hideAllButSelection) { toggleHiddenRegions(false, false); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == hideAllSelection) @@ -1164,14 +1166,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport.expandColSelection(sg, false); viewport.hideAllSelectedSeqs(); viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == showAllHidden) { viewport.showAllHiddenColumns(); viewport.showAllHiddenSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } else if (source == showGroupConsensus) @@ -1781,7 +1783,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } viewport.getAlignment().moveSelectedSequencesByOne(sg, up ? null : viewport.getHiddenRepSequences(), up); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); /* * Also move cDNA/protein complement sequences @@ -1793,7 +1795,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport, complement); complement.getAlignment().moveSelectedSequencesByOne(mappedSelection, up ? null : complement.getHiddenRepSequences(), up); - getSplitFrame().getComplement(this).alignPanel.paintAlignment(true); + getSplitFrame().getComplement(this).alignPanel.paintAlignment(true, + false); } } @@ -2226,7 +2229,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -2263,7 +2266,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2283,7 +2286,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2303,7 +2306,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, public void invertColSel_actionPerformed() { viewport.invertColumnSelection(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2595,19 +2598,19 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { viewport.setShowJVSuffix(seqLimits.getState()); alignPanel.fontChanged(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } protected void colourTextMenuItem_actionPerformed() { viewport.setColourText(colourTextMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } protected void displayNonconservedMenuItem_actionPerformed() { viewport.setShowUnconserved(displayNonconservedMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } protected void wrapMenuItem_actionPerformed() @@ -2617,7 +2620,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, scaleAbove.setEnabled(wrapMenuItem.getState()); scaleLeft.setEnabled(wrapMenuItem.getState()); scaleRight.setEnabled(wrapMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void overviewMenuItem_actionPerformed() @@ -2660,7 +2663,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { viewport.setGlobalColourScheme(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } protected void modifyPID_actionPerformed() @@ -2735,7 +2738,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void sortIDMenuItem_actionPerformed() @@ -2744,7 +2747,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, AlignmentSorter.sortByID(viewport.getAlignment()); addHistoryItem( new OrderCommand("ID Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void sortLengthMenuItem_actionPerformed() @@ -2753,7 +2756,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, AlignmentSorter.sortByLength(viewport.getAlignment()); addHistoryItem(new OrderCommand("Length Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } public void sortGroupMenuItem_actionPerformed() @@ -2762,7 +2765,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, AlignmentSorter.sortByGroup(viewport.getAlignment()); addHistoryItem(new OrderCommand("Group Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } @@ -2802,7 +2805,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, current.insertCharAt(Width - 1, viewport.getGapCharacter()); } } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } if ((viewport.getSelectionGroup() != null @@ -2866,7 +2869,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, current.insertCharAt(Width - 1, viewport.getGapCharacter()); } } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } @@ -2919,7 +2922,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, addHistoryItem(new OrderCommand(MessageManager .formatMessage("label.order_by_params", new String[] { title }), oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -2977,7 +2980,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, addHistoryItem(new OrderCommand(undoname, oldOrder, viewport.getAlignment())); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); return true; } diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index 8e333ba..906acc1 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -530,7 +530,7 @@ public class AlignmentPanel extends Panel vpRanges.scrollToWrappedVisible(start); } - paintAlignment(redrawOverview); + paintAlignment(redrawOverview, false); return true; } @@ -579,7 +579,7 @@ public class AlignmentPanel extends Panel apvscroll.addNotify(); hscroll.addNotify(); validate(); - paintAlignment(true); + paintAlignment(true, false); } /** @@ -930,7 +930,8 @@ public class AlignmentPanel extends Panel * Repaint the alignment and annotations, and, optionally, any overview window */ @Override - public void paintAlignment(boolean updateOverview) + public void paintAlignment(boolean updateOverview, + boolean updateStructures) { final AnnotationSorter sorter = new AnnotationSorter(getAlignment(), av.isShowAutocalculatedAbove()); @@ -938,13 +939,14 @@ public class AlignmentPanel extends Panel av.getSortAnnotationsBy()); repaint(); - if (updateOverview) + if (updateStructures) { - // TODO: determine if this paintAlignment changed structure colours jalview.structure.StructureSelectionManager .getStructureSelectionManager(av.applet) .sequenceColoursChanged(this); - + } + if (updateOverview) + { if (overviewPanel != null) { overviewPanel.updateOverviewImage(); diff --git a/src/jalview/appletgui/AnnotationColourChooser.java b/src/jalview/appletgui/AnnotationColourChooser.java index 8de751a..533226e 100644 --- a/src/jalview/appletgui/AnnotationColourChooser.java +++ b/src/jalview/appletgui/AnnotationColourChooser.java @@ -85,7 +85,7 @@ public class AnnotationColourChooser extends Panel implements oldcs = av.getGlobalColourScheme(); if (av.getAlignment().getGroups() != null) { - oldgroupColours = new HashMap(); + oldgroupColours = new HashMap<>(); for (SequenceGroup sg : ap.av.getAlignment().getGroups()) { oldgroupColours.put(sg, sg.getColourScheme()); @@ -180,8 +180,8 @@ public class AnnotationColourChooser extends Panel implements // TODO remove duplication with gui.AnnotationRowFilter // TODO add 'per sequence only' option / parameter - annotationLabels = new HashMap(); - Vector list = new Vector(); + annotationLabels = new HashMap<>(); + Vector list = new Vector<>(); AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation(); if (anns == null) { @@ -376,7 +376,7 @@ public class AnnotationColourChooser extends Panel implements else if (evt.getSource() == cancel) { reset(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); frame.setVisible(false); } @@ -417,7 +417,7 @@ public class AnnotationColourChooser extends Panel implements } currentAnnotation.threshold.value = slider.getValue() / 1000f; - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -559,7 +559,7 @@ public class AnnotationColourChooser extends Panel implements // update colours in linked windows ap.alignmentChanged(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } void reset() @@ -572,7 +572,7 @@ public class AnnotationColourChooser extends Panel implements sg.setColourScheme(oldgroupColours.get(sg)); } } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override @@ -588,7 +588,7 @@ public class AnnotationColourChooser extends Panel implements @Override public void mouseReleased(MouseEvent evt) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override diff --git a/src/jalview/appletgui/AnnotationColumnChooser.java b/src/jalview/appletgui/AnnotationColumnChooser.java index b4c1d54..7674de716 100644 --- a/src/jalview/appletgui/AnnotationColumnChooser.java +++ b/src/jalview/appletgui/AnnotationColumnChooser.java @@ -306,7 +306,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements av.getAlignment().setHiddenColumns(oldHidden); } av.sendSelection(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } @@ -348,7 +348,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements sliderDragging = false; valueChanged(true); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } }); } @@ -359,8 +359,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements if (slider.isEnabled()) { getCurrentAnnotation().threshold.value = slider.getValue() / 1000f; - updateView(); - ap.paintAlignment(false); + updateView(); // this also calls paintAlignment(true,true) } } @@ -515,7 +514,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements filterParams = null; av.setAnnotationColumnSelectionState(this); av.sendSelection(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } public HiddenColumns getOldHiddenColumns() diff --git a/src/jalview/appletgui/AnnotationLabels.java b/src/jalview/appletgui/AnnotationLabels.java index 2fb737a..d8f65a5 100755 --- a/src/jalview/appletgui/AnnotationLabels.java +++ b/src/jalview/appletgui/AnnotationLabels.java @@ -226,7 +226,8 @@ public class AnnotationLabels extends Panel ap.annotationPanel.adjustPanelHeight(); setSize(getSize().width, ap.annotationPanel.getSize().height); ap.validate(); - ap.paintAlignment(true); + // TODO: only paint if we needed to + ap.paintAlignment(true, true); } boolean editLabelDescription(AlignmentAnnotation annotation) @@ -548,7 +549,7 @@ public class AnnotationLabels extends Panel { ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } }); popup.add(cbmi); @@ -756,7 +757,7 @@ public class AnnotationLabels extends Panel } } } - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } @@ -813,7 +814,7 @@ public class AnnotationLabels extends Panel sg.addSequence(aa[selectedRow].sequenceRef, false); } ap.av.setSelectionGroup(sg); - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java index 6fe71de..c06f7b1 100755 --- a/src/jalview/appletgui/AnnotationPanel.java +++ b/src/jalview/appletgui/AnnotationPanel.java @@ -439,7 +439,8 @@ public class AnnotationPanel extends Panel graphStretchY = evt.getY(); av.calcPanelHeight(); needValidating = true; - ap.paintAlignment(true); + // TODO: only update overview visible geometry + ap.paintAlignment(true, false); } else { diff --git a/src/jalview/appletgui/AnnotationRowFilter.java b/src/jalview/appletgui/AnnotationRowFilter.java index 5efd177..c96dbab 100644 --- a/src/jalview/appletgui/AnnotationRowFilter.java +++ b/src/jalview/appletgui/AnnotationRowFilter.java @@ -114,7 +114,7 @@ public abstract class AnnotationRowFilter extends Panel public void cancel_actionPerformed(ActionEvent e) { reset(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); frame.setVisible(false); } diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index b35c079..df407d6 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -493,7 +493,7 @@ public class FeatureRenderer } // findAllFeatures(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); return true; } diff --git a/src/jalview/appletgui/FeatureSettings.java b/src/jalview/appletgui/FeatureSettings.java index 20d4d74..9a67499 100755 --- a/src/jalview/appletgui/FeatureSettings.java +++ b/src/jalview/appletgui/FeatureSettings.java @@ -377,8 +377,8 @@ public class FeatureSettings extends Panel // Group selection states void resetTable(boolean groupsChanged) { - List displayableTypes = new ArrayList(); - Set foundGroups = new HashSet(); + List displayableTypes = new ArrayList<>(); + Set foundGroups = new HashSet<>(); AlignmentI alignment = av.getAlignment(); @@ -391,7 +391,7 @@ public class FeatureSettings extends Panel * and keep track of which groups are visible */ Set groups = seq.getFeatures().getFeatureGroups(true); - Set visibleGroups = new HashSet(); + Set visibleGroups = new HashSet<>(); for (String group : groups) { // if (group == null || fr.checkGroupVisibility(group, true)) @@ -600,7 +600,7 @@ public class FeatureSettings extends Panel fr.setFeaturePriority(data); - ap.paintAlignment(updateOverview); + ap.paintAlignment(updateOverview, updateOverview); } MyCheckbox selectedCheck; @@ -680,7 +680,7 @@ public class FeatureSettings extends Panel { featurePanel.removeAll(); resetTable(false); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override @@ -732,7 +732,7 @@ public class FeatureSettings extends Panel public void adjustmentValueChanged(AdjustmentEvent evt) { fr.setTransparency((100 - transparency.getValue()) / 100f); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } class MyCheckbox extends Checkbox diff --git a/src/jalview/appletgui/FontChooser.java b/src/jalview/appletgui/FontChooser.java index c9a92b2..443ebce 100644 --- a/src/jalview/appletgui/FontChooser.java +++ b/src/jalview/appletgui/FontChooser.java @@ -261,7 +261,7 @@ public class FontChooser extends Panel implements ItemListener { ap.av.setCharWidth(oldCharWidth); } - ap.paintAlignment(true); + ap.paintAlignment(true, false); } else if (tp != null) { diff --git a/src/jalview/appletgui/IdPanel.java b/src/jalview/appletgui/IdPanel.java index 7d9d278..15e269c 100755 --- a/src/jalview/appletgui/IdPanel.java +++ b/src/jalview/appletgui/IdPanel.java @@ -70,7 +70,7 @@ public class IdPanel extends Panel // TODO: add in group link parameter // make a list of label,url pairs - HashMap urlList = new HashMap(); + HashMap urlList = new HashMap<>(); if (viewport.applet != null) { for (int i = 1; i < 10; i++) @@ -198,7 +198,7 @@ public class IdPanel extends Panel } lastid = seq; - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } @Override @@ -295,7 +295,7 @@ public class IdPanel extends Panel } else { - nlinks = new ArrayList(); + nlinks = new ArrayList<>(); } for (SequenceFeature sf : sq.getFeatures().getNonPositionalFeatures()) @@ -333,7 +333,7 @@ public class IdPanel extends Panel selectSeq(seq); } - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } void selectSeq(int seq) @@ -459,7 +459,7 @@ public class IdPanel extends Panel running = false; } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); try { Thread.sleep(100); diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index e74e1cd..0256055 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -200,7 +200,7 @@ public class OverviewPanel extends Panel implements Runnable, av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } diff --git a/src/jalview/appletgui/RedundancyPanel.java b/src/jalview/appletgui/RedundancyPanel.java index 2aba20c..bd36b0d 100644 --- a/src/jalview/appletgui/RedundancyPanel.java +++ b/src/jalview/appletgui/RedundancyPanel.java @@ -160,7 +160,7 @@ public class RedundancyPanel extends SliderPanel float value = slider.getValue(); - List redundantSequences = new ArrayList(); + List redundantSequences = new ArrayList<>(); for (int i = 0; i < redundancy.length; i++) { if (value <= redundancy[i]) @@ -247,7 +247,7 @@ public class RedundancyPanel extends SliderPanel ap.av.getAlignment().getSequences()); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); if (historyList.size() == 0) { diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java index 514c3f9..7d4150d 100755 --- a/src/jalview/appletgui/ScalePanel.java +++ b/src/jalview/appletgui/ScalePanel.java @@ -141,7 +141,7 @@ public class ScalePanel extends Panel sg.setStartRes(min); sg.setEndRes(max); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -167,7 +167,7 @@ public class ScalePanel extends Panel { av.showColumn(reveal[0]); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -183,7 +183,7 @@ public class ScalePanel extends Panel { av.showAllHiddenColumns(); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -208,7 +208,7 @@ public class ScalePanel extends Panel av.setSelectionGroup(null); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -239,7 +239,7 @@ public class ScalePanel extends Panel if (!stretchingGroup) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); return; } @@ -256,7 +256,7 @@ public class ScalePanel extends Panel } stretchingGroup = false; - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -285,7 +285,7 @@ public class ScalePanel extends Panel { stretchingGroup = true; cs.stretchGroup(res, sg, min, max); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index f36a8e2..55320ed 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -335,7 +335,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, sg.addSequence(sequence, false); av.setSelectionGroup(sg); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -983,7 +983,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, lastMousePress = evt.getPoint(); - ap.paintAlignment(false); + ap.paintAlignment(false, false); ap.annotationPanel.image = null; return; } @@ -1450,7 +1450,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, { if (links == null) { - links = new Vector(); + links = new Vector<>(); } links.addAll(sf.links); } @@ -1524,7 +1524,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, } } PaintRefresher.Refresh(ap, av.getSequenceSetId()); - ap.paintAlignment(needOverviewUpdate); + ap.paintAlignment(needOverviewUpdate, needOverviewUpdate); needOverviewUpdate = false; changeEndRes = false; changeStartRes = false; diff --git a/src/jalview/appletgui/SliderPanel.java b/src/jalview/appletgui/SliderPanel.java index 565ebe8..5841e80 100644 --- a/src/jalview/appletgui/SliderPanel.java +++ b/src/jalview/appletgui/SliderPanel.java @@ -441,7 +441,7 @@ public class SliderPanel extends Panel @Override public void mouseReleased(MouseEvent evt) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } @Override diff --git a/src/jalview/appletgui/SplitFrame.java b/src/jalview/appletgui/SplitFrame.java index ed531d3..777e307 100644 --- a/src/jalview/appletgui/SplitFrame.java +++ b/src/jalview/appletgui/SplitFrame.java @@ -187,9 +187,9 @@ public class SplitFrame extends EmbmenuFrame createSplitFrameWindow(embedded, applet); validate(); topFrame.alignPanel.adjustAnnotationHeight(); - topFrame.alignPanel.paintAlignment(true); + topFrame.alignPanel.paintAlignment(true, true); bottomFrame.alignPanel.adjustAnnotationHeight(); - bottomFrame.alignPanel.paintAlignment(true); + bottomFrame.alignPanel.paintAlignment(true, true); } /** diff --git a/src/jalview/appletgui/UserDefinedColours.java b/src/jalview/appletgui/UserDefinedColours.java index d1c0e1b..6831a73 100644 --- a/src/jalview/appletgui/UserDefinedColours.java +++ b/src/jalview/appletgui/UserDefinedColours.java @@ -64,7 +64,7 @@ public class UserDefinedColours extends Panel Button selectedButton; - Vector oldColours = new Vector(); + Vector oldColours = new Vector<>(); ColourSchemeI oldColourScheme; @@ -520,7 +520,7 @@ public class UserDefinedColours extends Panel ap.av.isIgnoreGapsConsensus()); } ap.seqPanel.seqCanvas.img = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); } else if (jmol != null) { @@ -599,7 +599,7 @@ public class UserDefinedColours extends Panel { ap.av.setGlobalColourScheme(oldColourScheme); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } frame.setVisible(false); diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java index dd05843..69e31cf 100644 --- a/src/jalview/controller/AlignViewController.java +++ b/src/jalview/controller/AlignViewController.java @@ -190,7 +190,7 @@ public class AlignViewController implements AlignViewControllerI if (changed) { viewport.setColumnSelection(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1) - bs.cardinality() @@ -215,7 +215,7 @@ public class AlignViewController implements AlignViewControllerI if (!extendCurrent) { cs.clear(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } } return false; @@ -337,7 +337,7 @@ public class AlignViewController implements AlignViewControllerI AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method); avcg.addHistoryItem(new OrderCommand(methodText, oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } @@ -375,7 +375,7 @@ public class AlignViewController implements AlignViewControllerI { avcg.getFeatureSettingsUI().discoverAllFeatureData(); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } return featuresFile; @@ -414,7 +414,7 @@ public class AlignViewController implements AlignViewControllerI if (changed) { viewport.setColumnSelection(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1) - bs.cardinality() @@ -438,7 +438,7 @@ public class AlignViewController implements AlignViewControllerI if (!extendCurrent) { cs.clear(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } } return false; diff --git a/src/jalview/datamodel/CigarArray.java b/src/jalview/datamodel/CigarArray.java index b6224c2..1723f1d 100644 --- a/src/jalview/datamodel/CigarArray.java +++ b/src/jalview/datamodel/CigarArray.java @@ -170,32 +170,30 @@ public class CigarArray extends CigarBase hideStart = region[0]; hideEnd = region[1]; // edit hidden regions to selection range - if (hideStart < last) + + // just move on if hideEnd is before last + if (hideEnd < last) { - if (hideEnd > last) - { - hideStart = last; - } - else - { - continue; - } + continue; } - + // exit if next region is after end if (hideStart > end) { break; } - if (hideEnd > end) + // truncate region at start if last falls in region + if ((hideStart < last) && (hideEnd >= last)) { - hideEnd = end; + hideStart = last; } - if (hideStart > hideEnd) + // truncate region at end if end falls in region + if (hideEnd > end) // already checked that hideStart<=end { - break; + hideEnd = end; } + /** * form operations... */ @@ -207,7 +205,7 @@ public class CigarArray extends CigarBase last = hideEnd + 1; } // Final match if necessary. - if (last < end) + if (last <= end) { addOperation(CigarArray.M, end - last + 1); } diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 98b0fb3..9680766 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -38,8 +38,6 @@ import java.util.List; import java.util.ListIterator; import java.util.Vector; -import com.stevesoft.pat.Regex; - import fr.orsay.lri.varna.models.rna.RNA; /** @@ -51,11 +49,6 @@ import fr.orsay.lri.varna.models.rna.RNA; */ public class Sequence extends ASequence implements SequenceI { - private static final Regex limitrx = new Regex( - "[/][0-9]{1,}[-][0-9]{1,}$"); - - private static final Regex endrx = new Regex("[0-9]{1,}$"); - SequenceI datasetSequence; String name; @@ -151,6 +144,10 @@ public class Sequence extends ASequence implements SequenceI checkValidRange(); } + /** + * If 'name' ends in /i-j, where i >= j > 0 are integers, extracts i and j as + * start and end respectively and removes the suffix from the name + */ void parseId() { if (name == null) @@ -159,17 +156,37 @@ public class Sequence extends ASequence implements SequenceI "POSSIBLE IMPLEMENTATION ERROR: null sequence name passed to constructor."); name = ""; } - // Does sequence have the /start-end signature? - if (limitrx.search(name)) + int slashPos = name.lastIndexOf('/'); + if (slashPos > -1 && slashPos < name.length() - 1) { - name = limitrx.left(); - endrx.search(limitrx.stringMatched()); - setStart(Integer.parseInt(limitrx.stringMatched().substring(1, - endrx.matchedFrom() - 1))); - setEnd(Integer.parseInt(endrx.stringMatched())); + String suffix = name.substring(slashPos + 1); + String[] range = suffix.split("-"); + if (range.length == 2) + { + try + { + int from = Integer.valueOf(range[0]); + int to = Integer.valueOf(range[1]); + if (from > 0 && to >= from) + { + name = name.substring(0, slashPos); + setStart(from); + setEnd(to); + checkValidRange(); + } + } catch (NumberFormatException e) + { + // leave name unchanged if suffix is invalid + } + } } } + /** + * Ensures that 'end' is not before the end of the sequence, that is, + * (end-start+1) is at least as long as the count of ungapped positions. Note + * that end is permitted to be beyond the end of the sequence data. + */ void checkValidRange() { // Note: JAL-774 : @@ -178,7 +195,7 @@ public class Sequence extends ASequence implements SequenceI int endRes = 0; for (int j = 0; j < sequence.length; j++) { - if (!jalview.util.Comparison.isGap(sequence[j])) + if (!Comparison.isGap(sequence[j])) { endRes++; } @@ -453,15 +470,15 @@ public class Sequence extends ASequence implements SequenceI } /** - * DOCUMENT ME! + * Sets the sequence name. If the name ends in /start-end, then the start-end + * values are parsed out and set, and the suffix is removed from the name. * - * @param name - * DOCUMENT ME! + * @param theName */ @Override - public void setName(String name) + public void setName(String theName) { - this.name = name; + this.name = theName; this.parseId(); } @@ -1884,7 +1901,9 @@ public class Sequence extends ASequence implements SequenceI * and we may have included adjacent or enclosing features; * remove any that are not enclosing, non-contact features */ - if (endPos > this.end || Comparison.isGap(sequence[toColumn - 1])) + boolean endColumnIsGapped = toColumn > 0 && toColumn <= sequence.length + && Comparison.isGap(sequence[toColumn - 1]); + if (endPos > this.end || endColumnIsGapped) { ListIterator it = result.listIterator(); while (it.hasNext()) diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index 115ecd4..afff4c2 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -205,8 +205,9 @@ public class EnsemblGene extends EnsemblSeqProxy } /** - * Converts a query, which may contain one or more gene or transcript - * identifiers, into a non-redundant list of gene identifiers. + * Converts a query, which may contain one or more gene, transcript, or + * external (to Ensembl) identifiers, into a non-redundant list of gene + * identifiers. * * @param accessions * @return @@ -217,54 +218,30 @@ public class EnsemblGene extends EnsemblSeqProxy for (String acc : accessions.split(getAccessionSeparator())) { - if (isGeneIdentifier(acc)) - { - if (!geneIds.contains(acc)) - { - geneIds.add(acc); - } - } - /* - * if given a transcript id, look up its gene parent + * First try lookup as an Ensembl (gene or transcript) identifier */ - else if (isTranscriptIdentifier(acc)) + String geneId = new EnsemblLookup(getDomain()).getGeneId(acc); + if (geneId != null) { - String geneId = new EnsemblLookup(getDomain()).getParent(acc); - if (geneId != null && !geneIds.contains(geneId)) + if (!geneIds.contains(geneId)) { geneIds.add(geneId); } } - else if (isProteinIdentifier(acc)) - { - String tscriptId = new EnsemblLookup(getDomain()).getParent(acc); - if (tscriptId != null) - { - String geneId = new EnsemblLookup(getDomain()) - .getParent(tscriptId); - - if (geneId != null && !geneIds.contains(geneId)) - { - geneIds.add(geneId); - } - } - // NOTE - acc is lost if it resembles an ENS.+ ID but isn't actually - // resolving to one... e.g. ENSMICP00000009241 - } - /* - * if given a gene or other external name, lookup and fetch - * the corresponding gene for all model organisms - */ else { + /* + * if given a gene or other external name, lookup and fetch + * the corresponding gene for all model organisms + */ List ids = new EnsemblSymbol(getDomain(), getDbSource(), - getDbVersion()).getIds(acc); - for (String geneId : ids) + getDbVersion()).getGeneIds(acc); + for (String id : ids) { - if (!geneIds.contains(geneId)) + if (!geneIds.contains(id)) { - geneIds.add(geneId); + geneIds.add(id); } } } @@ -273,30 +250,6 @@ public class EnsemblGene extends EnsemblSeqProxy } /** - * Attempts to get Ensembl stable identifiers for model organisms for a gene - * name by calling the xrefs symbol REST service to resolve the gene name. - * - * @param query - * @return - */ - protected String getGeneIdentifiersForName(String query) - { - List ids = new EnsemblSymbol(getDomain(), getDbSource(), - getDbVersion()).getIds(query); - if (ids != null) - { - for (String id : ids) - { - if (isGeneIdentifier(id)) - { - return id; - } - } - } - return null; - } - - /** * Constructs all transcripts for the gene, as identified by "transcript" * features whose Parent is the requested gene. The coding transcript * sequences (i.e. with introns omitted) are added to the alignment. diff --git a/src/jalview/ext/ensembl/EnsemblGenomes.java b/src/jalview/ext/ensembl/EnsemblGenomes.java index ef46a5b..b40df50 100644 --- a/src/jalview/ext/ensembl/EnsemblGenomes.java +++ b/src/jalview/ext/ensembl/EnsemblGenomes.java @@ -39,17 +39,12 @@ public class EnsemblGenomes extends EnsemblGene } @Override - public boolean isGeneIdentifier(String query) - { - return true; - } - - @Override public String getDbName() { return "EnsemblGenomes"; } + private String Wrong[]; @Override public String getTestQuery() { diff --git a/src/jalview/ext/ensembl/EnsemblLookup.java b/src/jalview/ext/ensembl/EnsemblLookup.java index 0968663..f314b0a 100644 --- a/src/jalview/ext/ensembl/EnsemblLookup.java +++ b/src/jalview/ext/ensembl/EnsemblLookup.java @@ -28,17 +28,16 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.List; +import java.util.function.Function; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; /** - * A client for the Ensembl lookup REST endpoint; used to find the Parent gene - * identifier given a transcript identifier. + * A client for the Ensembl lookup REST endpoint * * @author gmcarstairs - * */ public class EnsemblLookup extends EnsemblRestClient { @@ -46,6 +45,12 @@ public class EnsemblLookup extends EnsemblRestClient private static final String PARENT = "Parent"; + private static final String OBJECT_TYPE_TRANSLATION = "Translation"; + private static final String OBJECT_TYPE_TRANSCRIPT = "Transcript"; + private static final String ID = "id"; + private static final String OBJECT_TYPE_GENE = "Gene"; + private static final String OBJECT_TYPE = "object_type"; + /** * Default constructor (to use rest.ensembl.org) */ @@ -90,7 +95,7 @@ public class EnsemblLookup extends EnsemblRestClient protected URL getUrl(String identifier) { String url = getDomain() + "/lookup/id/" + identifier - + "?content-type=application/json"; + + CONTENT_TYPE_JSON; try { return new URL(url); @@ -125,9 +130,9 @@ public class EnsemblLookup extends EnsemblRestClient * @param identifier * @return */ - public String getParent(String identifier) + public String getGeneId(String identifier) { - return getAttribute(identifier, PARENT); + return getResult(identifier, br -> parseGeneId(br)); } /** @@ -139,7 +144,7 @@ public class EnsemblLookup extends EnsemblRestClient */ public String getSpecies(String identifier) { - return getAttribute(identifier, SPECIES); + return getResult(identifier, br -> getAttribute(br, SPECIES)); } /** @@ -147,7 +152,8 @@ public class EnsemblLookup extends EnsemblRestClient * @param attribute * @return */ - protected String getAttribute(String identifier, String attribute) + protected String getResult(String identifier, + Function parser) { List ids = Arrays.asList(new String[] { identifier }); @@ -159,7 +165,7 @@ public class EnsemblLookup extends EnsemblRestClient { br = getHttpResponse(url, ids); } - return (parseResponse(br, attribute)); + return br == null ? null : parser.apply(br); } catch (IOException e) { // ignore @@ -180,27 +186,75 @@ public class EnsemblLookup extends EnsemblRestClient } /** - * Parses the value of 'attribute' from the JSON response and returns the - * value, or null if not found + * Answers the value of 'attribute' from the JSON response, or null if not + * found * * @param br * @param attribute * @return - * @throws IOException */ - protected String parseResponse(BufferedReader br, String attribute) throws IOException + protected String getAttribute(BufferedReader br, String attribute) + { + String value = null; + JSONParser jp = new JSONParser(); + try + { + JSONObject val = (JSONObject) jp.parse(br); + value = val.get(attribute).toString(); + } catch (ParseException | NullPointerException | IOException e) + { + // ignore + } + return value; + } + + /** + * Parses the JSON response and returns the gene identifier, or null if not + * found. If the returned object_type is Gene, returns the id, if Transcript + * returns the Parent. If it is Translation (peptide identifier), then the + * Parent is the transcript identifier, so we redo the search with this value. + * + * @param br + * @return + */ + protected String parseGeneId(BufferedReader br) { - String parent = null; + String geneId = null; JSONParser jp = new JSONParser(); try { JSONObject val = (JSONObject) jp.parse(br); - parent = val.get(attribute).toString(); - } catch (ParseException | NullPointerException e) + String type = val.get(OBJECT_TYPE).toString(); + if (OBJECT_TYPE_GENE.equalsIgnoreCase(type)) + { + geneId = val.get(ID).toString(); + } + else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type)) + { + geneId = val.get(PARENT).toString(); + } + else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type)) + { + String transcriptId = val.get(PARENT).toString(); + try + { + geneId = getGeneId(transcriptId); + } catch (StackOverflowError e) + { + /* + * unlikely data condition error! + */ + System.err + .println("** Ensembl lookup " + + getUrl(transcriptId).toString() + + " looping on Parent!"); + } + } + } catch (ParseException | IOException e) { // ignore } - return parent; + return geneId; } } diff --git a/src/jalview/ext/ensembl/EnsemblProtein.java b/src/jalview/ext/ensembl/EnsemblProtein.java index 1554a0b..99006aa 100644 --- a/src/jalview/ext/ensembl/EnsemblProtein.java +++ b/src/jalview/ext/ensembl/EnsemblProtein.java @@ -23,8 +23,6 @@ package jalview.ext.ensembl; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceFeature; -import java.util.List; - import com.stevesoft.pat.Regex; /** diff --git a/src/jalview/ext/ensembl/EnsemblRestClient.java b/src/jalview/ext/ensembl/EnsemblRestClient.java index 7cabfbb..e3d1215 100644 --- a/src/jalview/ext/ensembl/EnsemblRestClient.java +++ b/src/jalview/ext/ensembl/EnsemblRestClient.java @@ -31,6 +31,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; +import java.net.ProtocolException; import java.net.URL; import java.util.HashMap; import java.util.List; @@ -42,8 +43,6 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; -import com.stevesoft.pat.Regex; - /** * Base class for Ensembl REST service clients * @@ -55,15 +54,21 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher private static final int CONNECT_TIMEOUT_MS = 10 * 1000; // 10 seconds + private static final int MAX_RETRIES = 3; + + private static final int HTTP_OK = 200; + + private static final int HTTP_OVERLOAD = 429; + /* * update these constants when Jalview has been checked / updated for * changes to Ensembl REST API (ref JAL-2105) * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log * @see http://rest.ensembl.org/info/rest?content-type=application/json */ - private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "5.0"; + private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "6.0"; - private static final String LATEST_ENSEMBL_REST_VERSION = "5.0"; + private static final String LATEST_ENSEMBL_REST_VERSION = "6.1"; private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log"; @@ -76,18 +81,11 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr - private static final Regex PROTEIN_REGEX = new Regex( - "(ENS)([A-Z]{3}|)P[0-9]{11}$"); - - private static final Regex TRANSCRIPT_REGEX = new Regex( - "(ENS)([A-Z]{3}|)T[0-9]{11}$"); - - private static final Regex GENE_REGEX = new Regex( - "(ENS)([A-Z]{3}|)G[0-9]{11}$"); + protected static final String CONTENT_TYPE_JSON = "?content-type=application/json"; static { - domainData = new HashMap(); + domainData = new HashMap<>(); domainData.put(ENSEMBL_REST, new EnsemblData(ENSEMBL_REST, LATEST_ENSEMBL_REST_VERSION)); domainData.put(ENSEMBL_GENOMES_REST, new EnsemblData( @@ -114,42 +112,6 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher setDomain(d); } - /** - * Answers true if the query matches the regular expression pattern for an - * Ensembl transcript stable identifier - * - * @param query - * @return - */ - public boolean isTranscriptIdentifier(String query) - { - return query == null ? false : TRANSCRIPT_REGEX.search(query); - } - - /** - * Answers true if the query matches the regular expression pattern for an - * Ensembl protein stable identifier - * - * @param query - * @return - */ - public boolean isProteinIdentifier(String query) - { - return query == null ? false : PROTEIN_REGEX.search(query); - } - - /** - * Answers true if the query matches the regular expression pattern for an - * Ensembl gene stable identifier - * - * @param query - * @return - */ - public boolean isGeneIdentifier(String query) - { - return query == null ? false : GENE_REGEX.search(query); - } - @Override public boolean queryInProgress() { @@ -204,21 +166,25 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher * @see http://rest.ensembl.org/documentation/info/ping * @return */ - private boolean checkEnsembl() + boolean checkEnsembl() { BufferedReader br = null; try { // note this format works for both ensembl and ensemblgenomes // info/ping.json works for ensembl only (March 2016) - URL ping = new URL( - getDomain() + "/info/ping?content-type=application/json"); + URL ping = new URL(getDomain() + "/info/ping" + CONTENT_TYPE_JSON); /* * expect {"ping":1} if ok * if ping takes more than 2 seconds to respond, treat as if unavailable */ br = getHttpResponse(ping, null, 2 * 1000); + if (br == null) + { + // error reponse status + return false; + } JSONParser jp = new JSONParser(); JSONObject val = (JSONObject) jp.parse(br); String pingString = val.get("ping").toString(); @@ -281,7 +247,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher } /** - * Writes the HTTP request and gets the response as a reader. + * Sends the HTTP request and gets the response as a reader * * @param url * @param ids @@ -295,7 +261,56 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher protected BufferedReader getHttpResponse(URL url, List ids, int readTimeout) throws IOException { - // long now = System.currentTimeMillis(); + int retriesLeft = MAX_RETRIES; + HttpURLConnection connection = null; + int responseCode = 0; + + while (retriesLeft > 0) + { + connection = tryConnection(url, ids, readTimeout); + responseCode = connection.getResponseCode(); + if (responseCode == HTTP_OVERLOAD) // 429 + { + retriesLeft--; + checkRetryAfter(connection); + } + else + { + retriesLeft = 0; + } + } + if (responseCode != HTTP_OK) // 200 + { + /* + * note: a GET request for an invalid id returns an error code e.g. 415 + * but POST request returns 200 and an empty Fasta response + */ + System.err.println("Response code " + responseCode + " for " + url); + return null; + } + + InputStream response = connection.getInputStream(); + + // System.out.println(getClass().getName() + " took " + // + (System.currentTimeMillis() - now) + "ms to fetch"); + + BufferedReader reader = null; + reader = new BufferedReader(new InputStreamReader(response, "UTF-8")); + return reader; + } + + /** + * @param url + * @param ids + * @param readTimeout + * @return + * @throws IOException + * @throws ProtocolException + */ + protected HttpURLConnection tryConnection(URL url, List ids, + int readTimeout) throws IOException, ProtocolException + { + // System.out.println(System.currentTimeMillis() + " " + url); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); /* @@ -320,77 +335,40 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher { writePostBody(connection, ids); } - - int responseCode = connection.getResponseCode(); - - if (responseCode != 200) - { - /* - * note: a GET request for an invalid id returns an error code e.g. 415 - * but POST request returns 200 and an empty Fasta response - */ - System.err.println("Response code " + responseCode + " for " + url); - return null; - } - // get content - InputStream response = connection.getInputStream(); - - // System.out.println(getClass().getName() + " took " - // + (System.currentTimeMillis() - now) + "ms to fetch"); - - checkRateLimits(connection); - - BufferedReader reader = null; - reader = new BufferedReader(new InputStreamReader(response, "UTF-8")); - return reader; + return connection; } /** - * Inspect response headers for any sign of server overload and respect any - * 'retry-after' directive + * Inspects response headers for a 'retry-after' directive, and waits for the + * directed period (if less than 10 seconds) * * @see https://github.com/Ensembl/ensembl-rest/wiki/Rate-Limits * @param connection */ - void checkRateLimits(HttpURLConnection connection) + void checkRetryAfter(HttpURLConnection connection) { - // number of requests allowed per time interval: - String limit = connection.getHeaderField("X-RateLimit-Limit"); - // length of quota time interval in seconds: - // String period = connection.getHeaderField("X-RateLimit-Period"); - // seconds remaining until usage quota is reset: - String reset = connection.getHeaderField("X-RateLimit-Reset"); - // number of requests remaining from quota for current period: - String remaining = connection.getHeaderField("X-RateLimit-Remaining"); - // number of seconds to wait before retrying (if remaining == 0) String retryDelay = connection.getHeaderField("Retry-After"); // to test: // retryDelay = "5"; - EnsemblData info = domainData.get(getDomain()); if (retryDelay != null) { - System.err.println("Ensembl REST service rate limit exceeded, wait " - + retryDelay + " seconds before retrying"); try { - info.retryAfter = System.currentTimeMillis() - + (1000 * Integer.valueOf(retryDelay)); - } catch (NumberFormatException e) + int retrySecs = Integer.valueOf(retryDelay); + if (retrySecs > 0 && retrySecs < 10) + { + System.err + .println("Ensembl REST service rate limit exceeded, waiting " + + retryDelay + " seconds before retrying"); + Thread.sleep(1000 * retrySecs); + } + } catch (NumberFormatException | InterruptedException e) { - System.err - .println("Unexpected value for Retry-After: " + retryDelay); + System.err.println("Error handling Retry-After: " + e.getMessage()); } } - else - { - info.retryAfter = 0; - // debug: - // System.out.println(String.format( - // "%s Ensembl requests remaining of %s (reset in %ss)", - // remaining, limit, reset)); - } } /** @@ -408,20 +386,6 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher long now = System.currentTimeMillis(); /* - * check if we are waiting for 'Retry-After' to expire - */ - if (info.retryAfter > now) - { - System.err.println("Still " + (1 + (info.retryAfter - now) / 1000) - + " secs to wait before retrying Ensembl"); - return false; - } - else - { - info.retryAfter = 0; - } - - /* * recheck if Ensembl is up if it was down, or the recheck period has elapsed */ boolean retestAvailability = (now @@ -497,9 +461,12 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher URL url = null; try { - url = new URL( - getDomain() + "/info/rest?content-type=application/json"); + url = new URL(getDomain() + "/info/rest" + CONTENT_TYPE_JSON); BufferedReader br = getHttpResponse(url, null); + if (br == null) + { + return; + } JSONObject val = (JSONObject) jp.parse(br); String version = val.get("release").toString(); String majorVersion = version.substring(0, version.indexOf(".")); @@ -558,18 +525,35 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher { JSONParser jp = new JSONParser(); URL url = null; + BufferedReader br = null; + try { - url = new URL( - getDomain() + "/info/data?content-type=application/json"); - BufferedReader br = getHttpResponse(url, null); - JSONObject val = (JSONObject) jp.parse(br); - JSONArray versions = (JSONArray) val.get("releases"); - domainData.get(getDomain()).dataVersion = versions.get(0).toString(); + url = new URL(getDomain() + "/info/data" + CONTENT_TYPE_JSON); + br = getHttpResponse(url, null); + if (br != null) + { + JSONObject val = (JSONObject) jp.parse(br); + JSONArray versions = (JSONArray) val.get("releases"); + domainData.get(getDomain()).dataVersion = versions.get(0) + .toString(); + } } catch (Throwable t) { System.err.println( "Error checking Ensembl data version: " + t.getMessage()); + } finally + { + if (br != null) + { + try + { + br.close(); + } catch (IOException e) + { + // ignore + } + } } } diff --git a/src/jalview/ext/ensembl/EnsemblSymbol.java b/src/jalview/ext/ensembl/EnsemblSymbol.java index 9f86731..75598a0 100644 --- a/src/jalview/ext/ensembl/EnsemblSymbol.java +++ b/src/jalview/ext/ensembl/EnsemblSymbol.java @@ -42,6 +42,10 @@ import org.json.simple.parser.ParseException; */ public class EnsemblSymbol extends EnsemblXref { + private static final String GENE = "gene"; + private static final String TYPE = "type"; + private static final String ID = "id"; + /** * Constructor given the target domain to fetch data from * @@ -73,8 +77,9 @@ public class EnsemblSymbol extends EnsemblXref while (rvals.hasNext()) { JSONObject val = (JSONObject) rvals.next(); - String id = val.get("id").toString(); - if (id != null && isGeneIdentifier(id)) + String id = val.get(ID).toString(); + String type = val.get(TYPE).toString(); + if (id != null && GENE.equals(type)) { result = id; break; @@ -87,12 +92,31 @@ public class EnsemblSymbol extends EnsemblXref return result; } - protected URL getUrl(String id, Species species) + /** + * Constructs the URL for the REST symbol endpoint + * + * @param id + * the accession id (Ensembl or external) + * @param species + * a species name recognisable by Ensembl + * @param type + * an optional type to filter the response (gene, transcript, + * translation) + * @return + */ + protected URL getUrl(String id, Species species, String... type) { - String url = getDomain() + "/xrefs/symbol/" + species.toString() + "/" - + id + "?content-type=application/json"; + StringBuilder sb = new StringBuilder(); + sb.append(getDomain()).append("/xrefs/symbol/") + .append(species.toString()).append("/").append(id) + .append(CONTENT_TYPE_JSON); + for (String t : type) + { + sb.append("&object_type=").append(t); + } try { + String url = sb.toString(); return new URL(url); } catch (MalformedURLException e) { @@ -107,7 +131,7 @@ public class EnsemblSymbol extends EnsemblXref * @param identifier * @return */ - public List getIds(String identifier) + public List getGeneIds(String identifier) { List result = new ArrayList(); List ids = new ArrayList(); @@ -119,19 +143,20 @@ public class EnsemblSymbol extends EnsemblXref { for (String query : queries) { - for (Species taxon : Species.values()) + for (Species taxon : Species.getModelOrganisms()) { - if (taxon.isModelOrganism()) + URL url = getUrl(query, taxon, GENE); + if (url != null) { - URL url = getUrl(query, taxon); - if (url != null) - { - br = getHttpResponse(url, ids); - } - String geneId = parseSymbolResponse(br); - if (geneId != null) + br = getHttpResponse(url, ids); + if (br != null) { - result.add(geneId); + String geneId = parseSymbolResponse(br); + System.out.println(url + " returned " + geneId); + if (geneId != null && !result.contains(geneId)) + { + result.add(geneId); + } } } } diff --git a/src/jalview/ext/ensembl/EnsemblXref.java b/src/jalview/ext/ensembl/EnsemblXref.java index c0b00b1..27c448e 100644 --- a/src/jalview/ext/ensembl/EnsemblXref.java +++ b/src/jalview/ext/ensembl/EnsemblXref.java @@ -124,8 +124,11 @@ class EnsemblXref extends EnsemblRestClient if (url != null) { br = getHttpResponse(url, ids); + if (br != null) + { + result = parseResponse(br); + } } - return (parseResponse(br)); } catch (IOException e) { // ignore @@ -168,16 +171,13 @@ class EnsemblXref extends EnsemblRestClient while (rvals.hasNext()) { JSONObject val = (JSONObject) rvals.next(); - String dbName = val.get("dbname").toString(); - if (dbName.equals(GO_GENE_ONTOLOGY)) - { - continue; - } + String db = val.get("dbname").toString(); String id = val.get("primary_id").toString(); - if (dbName != null && id != null) + if (db != null && id != null + && !GO_GENE_ONTOLOGY.equals(db)) { - dbName = DBRefUtils.getCanonicalName(dbName); - DBRefEntry dbref = new DBRefEntry(dbName, getXRefVersion(), id); + db = DBRefUtils.getCanonicalName(db); + DBRefEntry dbref = new DBRefEntry(db, getXRefVersion(), id); result.add(dbref); } } @@ -211,7 +211,7 @@ class EnsemblXref extends EnsemblRestClient protected URL getUrl(String identifier) { String url = getDomain() + "/xrefs/id/" + identifier - + "?content-type=application/json&all_levels=1"; + + CONTENT_TYPE_JSON + "&all_levels=1"; try { return new URL(url); diff --git a/src/jalview/ext/ensembl/Species.java b/src/jalview/ext/ensembl/Species.java index af01225..cc5465e 100644 --- a/src/jalview/ext/ensembl/Species.java +++ b/src/jalview/ext/ensembl/Species.java @@ -20,6 +20,9 @@ */ package jalview.ext.ensembl; +import java.util.HashSet; +import java.util.Set; + /** * Selected species identifiers used by Ensembl * @@ -38,6 +41,18 @@ enum Species chimpanzee(false), cat(false), zebrafish(true), chicken(true), dmelanogaster(true); + static Set modelOrganisms = new HashSet<>(); + + static + { + for (Species s : values()) + { + if (s.isModelOrganism()) + { + modelOrganisms.add(s); + } + } + } boolean modelOrganism; private Species(boolean model) @@ -49,4 +64,9 @@ enum Species { return modelOrganism; } + + public static Set getModelOrganisms() + { + return modelOrganisms; + } } diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 96dfcfe..50aba62 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -975,6 +975,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1], (String) data[0]); // also highlight in alignment + // deliberate fall through case HOVER: notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1], (String) data[0]); diff --git a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java index f923f7f..39d6704 100644 --- a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java +++ b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java @@ -120,7 +120,7 @@ public class AtomSpecModel for (String chain : modelData.keySet()) { - chain = chain.trim(); + chain = " ".equals(chain) ? chain : chain.trim(); List rangeList = modelData.get(chain); @@ -192,9 +192,10 @@ public class AtomSpecModel { sb.append(start).append("-").append(end); } - if (chain.length() > 0) - { - sb.append(".").append(chain); + + sb.append("."); + if (!" ".equals(chain)) { + sb.append(chain); } } } diff --git a/src/jalview/fts/service/pdb/PDBFTSPanel.java b/src/jalview/fts/service/pdb/PDBFTSPanel.java index 2a53ab9..19f7db4 100644 --- a/src/jalview/fts/service/pdb/PDBFTSPanel.java +++ b/src/jalview/fts/service/pdb/PDBFTSPanel.java @@ -43,13 +43,13 @@ public class PDBFTSPanel extends GFTSPanel private static final String PDB_FTS_CACHE_KEY = "CACHE.PDB_FTS"; - public PDBFTSPanel(SequenceFetcher seqFetcher) + public PDBFTSPanel(SequenceFetcher fetcher) { super(); pageLimit = PDBFTSRestClient.getInstance().getDefaultResponsePageSize(); - this.seqFetcher = seqFetcher; - this.progressIndicator = (seqFetcher == null) ? null - : seqFetcher.getProgressIndicator(); + this.seqFetcher = fetcher; + this.progressIndicator = (fetcher == null) ? null + : fetcher.getProgressIndicator(); } @Override @@ -86,15 +86,16 @@ public class PDBFTSPanel extends GFTSPanel request.setSearchTerm(searchTerm + ")"); request.setOffSet(offSet); request.setWantedFields(wantedFields); - FTSRestClientI pdbRestCleint = PDBFTSRestClient.getInstance(); + FTSRestClientI pdbRestClient = PDBFTSRestClient.getInstance(); FTSRestResponse resultList; try { - resultList = pdbRestCleint.executeRequest(request); + resultList = pdbRestClient.executeRequest(request); } catch (Exception e) { setErrorMessage(e.getMessage()); checkForErrors(); + setSearchInProgress(false); return; } diff --git a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java index 2dad2f7..020331a 100644 --- a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java +++ b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java @@ -40,18 +40,18 @@ public class UniprotFTSPanel extends GFTSPanel private static String defaultFTSFrameTitle = MessageManager .getString("label.uniprot_sequence_fetcher"); - private static Map tempUserPrefs = new HashMap(); + private static Map tempUserPrefs = new HashMap<>(); private static final String UNIPROT_FTS_CACHE_KEY = "CACHE.UNIPROT_FTS"; - public UniprotFTSPanel(SequenceFetcher seqFetcher) + public UniprotFTSPanel(SequenceFetcher fetcher) { super(); pageLimit = UniProtFTSRestClient.getInstance() .getDefaultResponsePageSize(); - this.seqFetcher = seqFetcher; - this.progressIndicator = (seqFetcher == null) ? null - : seqFetcher.getProgressIndicator(); + this.seqFetcher = fetcher; + this.progressIndicator = (fetcher == null) ? null + : fetcher.getProgressIndicator(); } @Override @@ -85,17 +85,17 @@ public class UniprotFTSPanel extends GFTSPanel request.setSearchTerm(searchTerm); request.setOffSet(offSet); request.setWantedFields(wantedFields); - FTSRestClientI uniProtRestCleint = UniProtFTSRestClient + FTSRestClientI uniProtRestClient = UniProtFTSRestClient .getInstance(); FTSRestResponse resultList; try { - resultList = uniProtRestCleint.executeRequest(request); + resultList = uniProtRestClient.executeRequest(request); } catch (Exception e) { - e.printStackTrace(); setErrorMessage(e.getMessage()); checkForErrors(); + setSearchInProgress(false); return; } @@ -183,7 +183,7 @@ public class UniprotFTSPanel extends GFTSPanel { disableActionButtons(); StringBuilder selectedIds = new StringBuilder(); - HashSet selectedIdsSet = new HashSet(); + HashSet selectedIdsSet = new HashSet<>(); int primaryKeyColIndex = 0; try { diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index f6b8392..bf94ec0 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -1713,7 +1713,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } viewport.getAlignment().moveSelectedSequencesByOne(sg, viewport.getHiddenRepSequences(), up); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } synchronized void slideSequences(boolean right, int size) @@ -2399,7 +2399,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -2425,7 +2425,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); } @@ -2450,7 +2450,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // JAL-2034 - should delegate to // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2481,7 +2481,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // alignPanel to decide if overview needs // updating. - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2490,7 +2490,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void invertColSel_actionPerformed(ActionEvent e) { viewport.invertColumnSelection(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); viewport.sendSelection(); } @@ -2870,21 +2870,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, alignPanel.getIdPanel().getIdCanvas() .setPreferredSize(alignPanel.calculateIdWidth()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } @Override public void idRightAlign_actionPerformed(ActionEvent e) { viewport.setRightAlignIds(idRightAlign.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } @Override public void centreColumnLabels_actionPerformed(ActionEvent e) { viewport.setCentreColumnLabels(centreColumnLabelsMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /* @@ -2917,7 +2917,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void colourTextMenuItem_actionPerformed(ActionEvent e) { viewport.setColourText(colourTextMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** @@ -2946,7 +2946,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void showAllColumns_actionPerformed(ActionEvent e) { viewport.showAllHiddenColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3050,7 +3050,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.expandColSelection(sg, false); viewport.hideAllSelectedSeqs(); viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3066,7 +3066,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { viewport.showAllHiddenColumns(); viewport.showAllHiddenSeqs(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3074,7 +3074,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void hideSelColumns_actionPerformed(ActionEvent e) { viewport.hideSelectedColumns(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@ -3095,7 +3095,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void scaleAbove_actionPerformed(ActionEvent e) { viewport.setScaleAboveWrapped(scaleAbove.isSelected()); - alignPanel.paintAlignment(true); + // TODO: do we actually need to update overview for scale above change ? + alignPanel.paintAlignment(true, false); } /** @@ -3108,7 +3109,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void scaleLeft_actionPerformed(ActionEvent e) { viewport.setScaleLeftWrapped(scaleLeft.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3121,7 +3122,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void scaleRight_actionPerformed(ActionEvent e) { viewport.setScaleRightWrapped(scaleRight.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3134,7 +3135,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void viewBoxesMenuItem_actionPerformed(ActionEvent e) { viewport.setShowBoxes(viewBoxesMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** @@ -3147,7 +3148,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void viewTextMenuItem_actionPerformed(ActionEvent e) { viewport.setShowText(viewTextMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** @@ -3160,7 +3161,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void renderGapsMenuItem_actionPerformed(ActionEvent e) { viewport.setRenderGaps(renderGapsMenuItem.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } public FeatureSettings featureSettings; @@ -3198,7 +3199,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void showSeqFeatures_actionPerformed(ActionEvent evt) { viewport.setShowSequenceFeatures(showSeqFeatures.isSelected()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } /** @@ -3355,7 +3356,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.setGlobalColourScheme(cs); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } /** @@ -3440,7 +3441,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment().getSequenceAt(0)); addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3456,7 +3457,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignmentSorter.sortByID(viewport.getAlignment()); addHistoryItem( new OrderCommand("ID Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3472,7 +3473,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignmentSorter.sortByLength(viewport.getAlignment()); addHistoryItem(new OrderCommand("Length Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3489,7 +3490,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addHistoryItem(new OrderCommand("Group Sort", oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } /** @@ -3646,7 +3647,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addHistoryItem(new OrderCommand(order.getName(), oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } }); } @@ -3675,7 +3676,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment());// ,viewport.getSelectionGroup()); addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder, viewport.getAlignment())); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); } }); } @@ -3790,7 +3791,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addHistoryItem(new OrderCommand(undoname, oldOrder, viewport.getAlignment())); } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, false); return true; } @@ -4505,7 +4506,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, assocfiles++; } } - alignPanel.paintAlignment(true); + // TODO: do we need to update overview ? only if features are + // shown I guess + alignPanel.paintAlignment(true, false); } } } @@ -4652,7 +4655,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { if (parseFeaturesFile(file, sourceType)) { - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } else @@ -4667,7 +4670,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, alignPanel.adjustAnnotationHeight(); viewport.updateSequenceIdColours(); buildSortByAnnotationScoresMenu(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } catch (Exception ex) { @@ -5192,7 +5195,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void showUnconservedMenuItem_actionPerformed(ActionEvent e) { viewport.setShowUnconserved(showNonconservedMenuItem.getState()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /* @@ -5281,7 +5284,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -5293,7 +5296,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.getAlignment().setSeqrep(null); PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); } } @@ -5383,7 +5386,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder()); this.alignPanel.av .setShowAutocalculatedAbove(isShowAutoCalculatedAbove()); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false, false); } /** diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index c700635..b2c4809 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -97,9 +97,6 @@ public class AlignmentPanel extends GAlignmentPanel implements private AnnotationLabels alabels; - // this value is set false when selection area being dragged - boolean fastPaint = true; - private int hextent = 0; private int vextent = 0; @@ -214,7 +211,8 @@ public class AlignmentPanel extends GAlignmentPanel implements alignFrame.updateEditMenuBar(); - paintAlignment(true); + // no idea if we need to update structure + paintAlignment(true, true); } @@ -481,7 +479,7 @@ public class AlignmentPanel extends GAlignmentPanel implements scrollNeeded = vpRanges.scrollToWrappedVisible(start); } - paintAlignment(redrawOverview); + paintAlignment(redrawOverview, false); return scrollNeeded; } @@ -538,7 +536,9 @@ public class AlignmentPanel extends GAlignmentPanel implements } validateAnnotationDimensions(true); addNotify(); - paintAlignment(true); + // TODO: many places call this method and also paintAlignment with various + // different settings. this means multiple redraws are triggered... + paintAlignment(true, false); } /** @@ -632,9 +632,9 @@ public class AlignmentPanel extends GAlignmentPanel implements } else { - int widthInRes = (canvasWidth / av.getCharWidth()) - 1; + int widthInRes = (canvasWidth / av.getCharWidth()); int heightInSeq = (getSeqPanel().seqCanvas.getHeight() - / av.getCharHeight()) - 1; + / av.getCharHeight()); vpRanges.setViewportWidth(widthInRes); vpRanges.setViewportHeight(heightInSeq); @@ -768,10 +768,7 @@ public class AlignmentPanel extends GAlignmentPanel implements } vpRanges.setViewportStartAndHeight(y, height); } - if (!fastPaint) - { - repaint(); - } + repaint(); } /** @@ -837,12 +834,12 @@ public class AlignmentPanel extends GAlignmentPanel implements repaint(); } - /** - * Repaint the alignment including the annotations and overview panels (if - * shown). + /* (non-Javadoc) + * @see jalview.api.AlignmentViewPanel#paintAlignment(boolean) */ @Override - public void paintAlignment(boolean updateOverview) + public void paintAlignment(boolean updateOverview, + boolean updateStructures) { final AnnotationSorter sorter = new AnnotationSorter(getAlignment(), av.isShowAutocalculatedAbove()); @@ -850,10 +847,12 @@ public class AlignmentPanel extends GAlignmentPanel implements av.getSortAnnotationsBy()); repaint(); - if (updateOverview) + if (updateStructures) { - // TODO: determine if this paintAlignment changed structure colours av.getStructureSelectionManager().sequenceColoursChanged(this); + } + if (updateOverview) + { if (overviewPanel != null) { @@ -1866,7 +1865,7 @@ public class AlignmentPanel extends GAlignmentPanel implements if (adjustHeight) { // sort, repaint, update overview - paintAlignment(true); + paintAlignment(true, false); } else { diff --git a/src/jalview/gui/AnnotationChooser.java b/src/jalview/gui/AnnotationChooser.java index 26796de..84883d7 100644 --- a/src/jalview/gui/AnnotationChooser.java +++ b/src/jalview/gui/AnnotationChooser.java @@ -82,7 +82,7 @@ public class AnnotationChooser extends JPanel private boolean applyToUnselectedSequences; // currently selected 'annotation type' checkboxes - private Map selectedTypes = new HashMap(); + private Map selectedTypes = new HashMap<>(); /** * Constructor. @@ -202,7 +202,7 @@ public class AnnotationChooser extends JPanel // this.ap.alabels.setSize(this.ap.alabels.getSize().width, // this.ap.annotationPanel.getSize().height); // this.ap.validate(); - this.ap.paintAlignment(true); + this.ap.paintAlignment(true, false); } /** @@ -233,7 +233,7 @@ public class AnnotationChooser extends JPanel // this.ap.alabels.setSize(this.ap.alabels.getSize().width, // this.ap.annotationPanel.getSize().height); // this.ap.validate(); - this.ap.paintAlignment(true); + this.ap.paintAlignment(true, false); } /** @@ -251,7 +251,7 @@ public class AnnotationChooser extends JPanel this.ap.updateAnnotation(); // this.ap.annotationPanel.adjustPanelHeight(); - this.ap.paintAlignment(true); + this.ap.paintAlignment(true, false); } /** @@ -356,7 +356,7 @@ public class AnnotationChooser extends JPanel public static List getAnnotationTypes(AlignmentI alignment, boolean sequenceSpecificOnly) { - List result = new ArrayList(); + List result = new ArrayList<>(); for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation()) { if (!sequenceSpecificOnly || aa.sequenceRef != null) diff --git a/src/jalview/gui/AnnotationColourChooser.java b/src/jalview/gui/AnnotationColourChooser.java index 8d123bb..153f70c 100644 --- a/src/jalview/gui/AnnotationColourChooser.java +++ b/src/jalview/gui/AnnotationColourChooser.java @@ -79,7 +79,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter oldcs = av.getGlobalColourScheme(); if (av.getAlignment().getGroups() != null) { - oldgroupColours = new Hashtable(); + oldgroupColours = new Hashtable<>(); for (SequenceGroup sg : ap.av.getAlignment().getGroups()) { if (sg.getColourScheme() != null) @@ -122,7 +122,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter } Vector annotItems = getAnnotationItems( seqAssociated.isSelected()); - annotations = new JComboBox(annotItems); + annotations = new JComboBox<>(annotItems); populateThresholdComboBox(threshold); @@ -341,7 +341,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter getCurrentAnnotation().threshold.value = slider.getValue() / 1000f; propagateSeqAssociatedThreshold(updateAllAnnotation, getCurrentAnnotation()); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -415,12 +415,9 @@ public class AnnotationColourChooser extends AnnotationRowFilter colorAlignmentContaining(getCurrentAnnotation(), selectedThresholdItem); ap.alignmentChanged(); - // ensure all associated views (overviews, structures, etc) are notified of - // updated colours. - ap.paintAlignment(true); } - protected boolean colorAlignmentContaining(AlignmentAnnotation currentAnn, + protected void colorAlignmentContaining(AlignmentAnnotation currentAnn, int selectedThresholdOption) { @@ -460,7 +457,6 @@ public class AnnotationColourChooser extends AnnotationRowFilter acg.getInstance(sg, ap.av.getHiddenRepSequences())); } } - return false; } } diff --git a/src/jalview/gui/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java index 84b2c6f..020e027 100644 --- a/src/jalview/gui/AnnotationColumnChooser.java +++ b/src/jalview/gui/AnnotationColumnChooser.java @@ -254,7 +254,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter av.getAlignment().setHiddenColumns(oldHidden); } av.sendSelection(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } @@ -267,7 +267,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter updateView(); propagateSeqAssociatedThreshold(updateAllAnnotation, getCurrentAnnotation()); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } @@ -391,7 +391,8 @@ public class AnnotationColumnChooser extends AnnotationRowFilter av.getColumnSelection().filterAnnotations( getCurrentAnnotation().annotations, filterParams); - if (getActionOption() == ACTION_OPTION_HIDE) + boolean hideCols = getActionOption() == ACTION_OPTION_HIDE; + if (hideCols) { av.hideSelectedColumns(); } @@ -399,7 +400,8 @@ public class AnnotationColumnChooser extends AnnotationRowFilter filterParams = null; av.setAnnotationColumnSelectionState(this); - ap.paintAlignment(true); + // only update overview and structures if columns were hidden + ap.paintAlignment(hideCols, hideCols); } public HiddenColumns getOldHiddenColumns() diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index d07cae2..b94a615 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -705,7 +705,7 @@ public class AnnotationLabels extends JPanel d = ap.annotationSpaceFillerHolder.getPreferredSize(); ap.annotationSpaceFillerHolder .setPreferredSize(new Dimension(d.width, d.height - dif)); - ap.paintAlignment(true); + ap.paintAlignment(true, false); } ap.addNotify(); @@ -855,7 +855,7 @@ public class AnnotationLabels extends JPanel } } - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } @@ -912,7 +912,7 @@ public class AnnotationLabels extends JPanel sg.addSequence(aa[selectedRow].sequenceRef, false); } ap.av.setSelectionGroup(sg); - ap.paintAlignment(false); + ap.paintAlignment(false, false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); } diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index be8f5f6..12d1369 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -670,7 +670,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } graphStretchY = evt.getY(); adjustPanelHeight(); - ap.paintAlignment(true); + ap.paintAlignment(false, false); } else { diff --git a/src/jalview/gui/AnnotationRowFilter.java b/src/jalview/gui/AnnotationRowFilter.java index 8b2486f..71ad6a5 100644 --- a/src/jalview/gui/AnnotationRowFilter.java +++ b/src/jalview/gui/AnnotationRowFilter.java @@ -80,7 +80,7 @@ public abstract class AnnotationRowFilter extends JPanel */ protected boolean sliderDragging = false; - protected JComboBox threshold = new JComboBox(); + protected JComboBox threshold = new JComboBox<>(); protected JComboBox annotations; @@ -177,7 +177,6 @@ public abstract class AnnotationRowFilter extends JPanel sliderDragging = false; valueChanged(true); } - ap.paintAlignment(true); } }); } @@ -192,9 +191,9 @@ public abstract class AnnotationRowFilter extends JPanel */ public Vector getAnnotationItems(boolean isSeqAssociated) { - annotationLabels = new HashMap(); + annotationLabels = new HashMap<>(); - Vector list = new Vector(); + Vector list = new Vector<>(); int index = 1; int[] anmap = new int[av.getAlignment() .getAlignmentAnnotation().length]; @@ -271,7 +270,7 @@ public abstract class AnnotationRowFilter extends JPanel public void cancel_actionPerformed() { reset(); - ap.paintAlignment(true); + ap.paintAlignment(true, true); try { frame.setClosed(true); @@ -413,6 +412,11 @@ public abstract class AnnotationRowFilter extends JPanel this.currentAnnotation = annotation; } + /** + * update associated view model and trigger any necessary repaints. + * + * @param updateAllAnnotation + */ protected abstract void valueChanged(boolean updateAllAnnotation); protected abstract void updateView(); diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java index b27328d..d8db546 100644 --- a/src/jalview/gui/FeatureColourChooser.java +++ b/src/jalview/gui/FeatureColourChooser.java @@ -147,7 +147,7 @@ public class FeatureColourChooser extends JalviewDialog */ if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } }); @@ -396,9 +396,9 @@ public class FeatureColourChooser extends JalviewDialog * feature type, and repaints the alignment, and optionally the Overview * and/or structure viewer if open * - * @param updateOverview + * @param updateStructsAndOverview */ - void changeColour(boolean updateOverview) + void changeColour(boolean updateStructsAndOverview) { // Check if combobox is still adjusting if (adjusting) @@ -507,7 +507,7 @@ public class FeatureColourChooser extends JalviewDialog } fr.setColour(type, acg); cs = acg; - ap.paintAlignment(updateOverview); + ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview); } @Override @@ -539,7 +539,7 @@ public class FeatureColourChooser extends JalviewDialog void reset() { fr.setColour(type, oldcs); - ap.paintAlignment(true); + ap.paintAlignment(true, true); cs = null; } @@ -565,7 +565,7 @@ public class FeatureColourChooser extends JalviewDialog /* * force repaint of any Overview window or structure */ - ap.paintAlignment(true); + ap.paintAlignment(true, true); } catch (NumberFormatException ex) { } diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java index 17f5a71..9c4b009 100644 --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@ -243,7 +243,7 @@ public class FeatureRenderer JPanel choosePanel = new JPanel(); choosePanel.add(new JLabel( MessageManager.getString("label.select_feature") + ":")); - final JComboBox overlaps = new JComboBox(); + final JComboBox overlaps = new JComboBox<>(); List added = new ArrayList<>(); for (SequenceFeature sf : features) { @@ -487,7 +487,7 @@ public class FeatureRenderer featuresAdded(); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); return true; } @@ -497,7 +497,7 @@ public class FeatureRenderer } } - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(true, true); return true; } diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index 0963b31..b768339 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -131,6 +131,11 @@ public class FeatureSettings extends JPanel private static final int MIN_WIDTH = 400; private static final int MIN_HEIGHT = 400; + + /** + * when true, constructor is still executing - so ignore UI events + */ + protected volatile boolean inConstruction = true; /** * Constructor @@ -303,6 +308,7 @@ public class FeatureSettings extends JPanel }; }); frame.setLayer(JLayeredPane.PALETTE_LAYER); + inConstruction = false; } protected void popupSort(final int selectedRow, final String type, @@ -485,7 +491,7 @@ public class FeatureSettings extends JPanel @Override synchronized public void discoverAllFeatureData() { - Set allGroups = new HashSet(); + Set allGroups = new HashSet<>(); AlignmentI alignment = af.getViewport().getAlignment(); for (int i = 0; i < alignment.getHeight(); i++) @@ -557,12 +563,12 @@ public class FeatureSettings extends JPanel return; } resettingTable = true; - typeWidth = new Hashtable(); + typeWidth = new Hashtable<>(); // TODO: change avWidth calculation to 'per-sequence' average and use long // rather than float - Set displayableTypes = new HashSet(); - Set foundGroups = new HashSet(); + Set displayableTypes = new HashSet<>(); + Set foundGroups = new HashSet<>(); /* * determine which feature types may be visible depending on @@ -578,7 +584,7 @@ public class FeatureSettings extends JPanel * and keep track of which groups are visible */ Set groups = seq.getFeatures().getFeatureGroups(true); - Set visibleGroups = new HashSet(); + Set visibleGroups = new HashSet<>(); for (String group : groups) { if (group == null || checkGroupState(group)) @@ -1058,7 +1064,7 @@ public class FeatureSettings extends JPanel { if (fr.setFeaturePriority(data, visibleNew)) { - af.alignPanel.paintAlignment(true); + af.alignPanel.paintAlignment(true, true); } } @@ -1237,8 +1243,11 @@ public class FeatureSettings extends JPanel @Override public void stateChanged(ChangeEvent evt) { - fr.setTransparency((100 - transparency.getValue()) / 100f); - af.alignPanel.paintAlignment(true); + if (!inConstruction) + { + fr.setTransparency((100 - transparency.getValue()) / 100f); + af.alignPanel.paintAlignment(true,true); + } } }); diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java index 6cddcca..80ac189 100755 --- a/src/jalview/gui/FontChooser.java +++ b/src/jalview/gui/FontChooser.java @@ -183,7 +183,7 @@ public class FontChooser extends GFontChooser { ap.av.antiAlias = smoothFont.isSelected(); ap.getAnnotationPanel().image = null; - ap.paintAlignment(true); + ap.paintAlignment(true, false); if (ap.av.getCodingComplement() != null && ap.av.isProteinFontAsCdna()) { ((AlignViewport) ap.av @@ -235,7 +235,7 @@ public class FontChooser extends GFontChooser ap.av.setScaleProteinAsCdna(oldProteinScale); ap.av.setProteinFontAsCdna(oldMirrorFont); ap.av.antiAlias = oldSmoothFont; - ap.paintAlignment(true); + ap.paintAlignment(true, false); if (scaleAsCdna.isVisible() && scaleAsCdna.isEnabled()) { diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index f0aefb1..35fd1b4 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -138,7 +138,7 @@ public class IdPanel extends JPanel } lastid = seq; - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } /** @@ -313,7 +313,7 @@ public class IdPanel extends JPanel av.isSelectionGroupChanged(true); - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); } /** @@ -508,7 +508,7 @@ public class IdPanel extends JPanel running = false; } - alignPanel.paintAlignment(false); + alignPanel.paintAlignment(false, false); try { diff --git a/src/jalview/gui/IdwidthAdjuster.java b/src/jalview/gui/IdwidthAdjuster.java index 3c4107f..8400543 100755 --- a/src/jalview/gui/IdwidthAdjuster.java +++ b/src/jalview/gui/IdwidthAdjuster.java @@ -75,6 +75,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mousePressed(MouseEvent evt) { oldX = evt.getX(); @@ -86,6 +87,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseReleased(MouseEvent evt) { active = false; @@ -112,6 +114,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseEntered(MouseEvent evt) { active = true; @@ -124,6 +127,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseExited(MouseEvent evt) { active = false; @@ -136,6 +140,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseDragged(MouseEvent evt) { active = true; @@ -149,7 +154,7 @@ public class IdwidthAdjuster extends JPanel { viewport.setIdWidth(newWidth); - ap.paintAlignment(true); + ap.paintAlignment(true, false); } oldX = evt.getX(); @@ -161,6 +166,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseMoved(MouseEvent evt) { } @@ -171,6 +177,7 @@ public class IdwidthAdjuster extends JPanel * @param evt * DOCUMENT ME! */ + @Override public void mouseClicked(MouseEvent evt) { } @@ -181,6 +188,7 @@ public class IdwidthAdjuster extends JPanel * @param g * DOCUMENT ME! */ + @Override public void paintComponent(Graphics g) { g.setColor(Color.white); diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 33c86bc..40f5764 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -1842,7 +1842,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener } sequence.setName(dialog.getName().replace(' ', '_')); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } sequence.setDescription(dialog.getDescription()); diff --git a/src/jalview/gui/RedundancyPanel.java b/src/jalview/gui/RedundancyPanel.java index 8bf2fba..c4390c0 100755 --- a/src/jalview/gui/RedundancyPanel.java +++ b/src/jalview/gui/RedundancyPanel.java @@ -54,7 +54,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable AlignmentPanel ap; - Stack historyList = new Stack(); + Stack historyList = new Stack<>(); // simpler than synching with alignFrame. @@ -194,7 +194,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable } float value = slider.getValue(); - List redundantSequences = new ArrayList(); + List redundantSequences = new ArrayList<>(); for (int i = 0; i < redundancy.length; i++) { if (value <= redundancy[i]) @@ -295,7 +295,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable af.updateEditMenuBar(); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); if (historyList.size() == 0) { diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index 1db4051..e677769 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -168,7 +168,7 @@ public class ScalePanel extends JPanel { av.showColumn(reveal[0]); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -184,7 +184,7 @@ public class ScalePanel extends JPanel { av.showAllHiddenColumns(); reveal = null; - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -208,7 +208,7 @@ public class ScalePanel extends JPanel av.setSelectionGroup(null); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); av.sendSelection(); } }); @@ -260,7 +260,7 @@ public class ScalePanel extends JPanel sg.setEndRes(max); } av.setSelectionGroup(sg); - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -297,7 +297,7 @@ public class ScalePanel extends JPanel } else { - ap.paintAlignment(false); + ap.paintAlignment(false, false); } return; } @@ -316,7 +316,7 @@ public class ScalePanel extends JPanel } } stretchingGroup = false; - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -346,7 +346,7 @@ public class ScalePanel extends JPanel { stretchingGroup = true; cs.stretchGroup(res, sg, min, max); - ap.paintAlignment(false); + ap.paintAlignment(false, false); } } diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 4e896a0..2a9c704 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -27,6 +27,7 @@ import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.renderer.ScaleRenderer; import jalview.renderer.ScaleRenderer.ScaleMark; +import jalview.util.Comparison; import jalview.viewmodel.ViewportListenerI; import jalview.viewmodel.ViewportRanges; @@ -46,53 +47,56 @@ import java.util.List; import javax.swing.JComponent; /** - * DOCUMENT ME! + * The Swing component on which the alignment sequences, and annotations (if + * shown), are drawn. This includes scales above, left and right (if shown) in + * Wrapped mode, but not the scale above in Unwrapped mode. * - * @author $author$ - * @version $Revision$ */ public class SeqCanvas extends JComponent implements ViewportListenerI { - private static String ZEROS = "0000000000"; + private static final String ZEROS = "0000000000"; final FeatureRenderer fr; - final SequenceRenderer seqRdr; - BufferedImage img; - Graphics2D gg; - AlignViewport av; - boolean fastPaint = false; + int cursorX = 0; - int labelWidthWest; + int cursorY = 0; - int labelWidthEast; + private final SequenceRenderer seqRdr; - int cursorX = 0; + private boolean fastPaint = false; - int cursorY = 0; + private boolean fastpainting = false; - int charHeight = 0; + private AnnotationPanel annotations; - int charWidth = 0; + /* + * measurements for drawing a wrapped alignment + */ + private int labelWidthEast; // label right width in pixels if shown + + private int labelWidthWest; // label left width in pixels if shown - boolean fastpainting = false; + private int wrappedSpaceAboveAlignment; // gap between widths - AnnotationPanel annotations; + private int wrappedRepeatHeightPx; // height in pixels of wrapped width + + private int wrappedVisibleWidths; // number of wrapped widths displayed + + private Graphics2D gg; /** * Creates a new SeqCanvas object. * - * @param av - * DOCUMENT ME! + * @param ap */ public SeqCanvas(AlignmentPanel ap) { this.av = ap.av; - updateViewport(); fr = new FeatureRenderer(ap); seqRdr = new SequenceRenderer(av); setLayout(new BorderLayout()); @@ -112,29 +116,36 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return fr; } - private void updateViewport() - { - charHeight = av.getCharHeight(); - charWidth = av.getCharWidth(); - } - /** - * DOCUMENT ME! + * Draws the scale above a region of a wrapped alignment, consisting of a + * column number every major interval (10 columns). * * @param g - * DOCUMENT ME! + * the graphics context to draw on, positioned at the start (bottom + * left) of the line on which to draw any scale marks * @param startx - * DOCUMENT ME! + * start alignment column (0..) * @param endx - * DOCUMENT ME! + * end alignment column (0..) * @param ypos - * DOCUMENT ME! + * y offset to draw at */ private void drawNorthScale(Graphics g, int startx, int endx, int ypos) { - updateViewport(); - for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx, - endx)) + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + + /* + * white fill the scale space (for the fastPaint case) + */ + g.setColor(Color.white); + g.fillRect(0, ypos - charHeight - charHeight / 2, getWidth(), + charHeight * 3 / 2 + 2); + g.setColor(Color.black); + + List marks = new ScaleRenderer().calculateMarks(av, startx, + endx); + for (ScaleMark mark : marks) { int mpos = mark.column; // (i - startx - 1) if (mpos < 0) @@ -149,137 +160,119 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2)); } - g.drawLine((mpos * charWidth) + (charWidth / 2), - (ypos + 2) - (charHeight / 2), - (mpos * charWidth) + (charWidth / 2), ypos - 2); + + /* + * draw a tick mark below the column number, centred on the column; + * height of tick mark is 4 pixels less than half a character + */ + int xpos = (mpos * charWidth) + (charWidth / 2); + g.drawLine(xpos, (ypos + 2) - (charHeight / 2), xpos, ypos - 2); } } } /** - * DOCUMENT ME! + * Draw the scale to the left or right of a wrapped alignment * * @param g - * DOCUMENT ME! + * graphics context, positioned at the start of the scale to be drawn * @param startx - * DOCUMENT ME! + * first column of wrapped width (0.. excluding any hidden columns) * @param endx - * DOCUMENT ME! + * last column of wrapped width (0.. excluding any hidden columns) * @param ypos - * DOCUMENT ME! + * vertical offset at which to begin the scale + * @param left + * if true, scale is left of residues, if false, scale is right */ - void drawWestScale(Graphics g, int startx, int endx, int ypos) + void drawVerticalScale(Graphics g, final int startx, final int endx, + final int ypos, final boolean left) { - FontMetrics fm = getFontMetrics(av.getFont()); - ypos += charHeight; + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); - if (av.hasHiddenColumns()) - { - startx = av.getAlignment().getHiddenColumns() - .adjustForHiddenColumns(startx); - endx = av.getAlignment().getHiddenColumns() - .adjustForHiddenColumns(endx); - } + int yPos = ypos + charHeight; + int startX = startx; + int endX = endx; - int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns(); + startX = hiddenColumns.adjustForHiddenColumns(startx); + endX = hiddenColumns.adjustForHiddenColumns(endx); } + FontMetrics fm = getFontMetrics(av.getFont()); - // WEST SCALE for (int i = 0; i < av.getAlignment().getHeight(); i++) { SequenceI seq = av.getAlignment().getSequenceAt(i); - int index = startx; - int value = -1; - while (index < endx) + /* + * find sequence position of first non-gapped position - + * to the right if scale left, to the left if scale right + */ + int index = left ? startX : endX; + int value = -1; + while (index >= startX && index <= endX) { - if (jalview.util.Comparison.isGap(seq.getCharAt(index))) + if (!Comparison.isGap(seq.getCharAt(index))) + { + value = seq.findPosition(index); + break; + } + if (left) { index++; - - continue; } - - value = av.getAlignment().getSequenceAt(i).findPosition(index); - - break; - } - - if (value != -1) - { - int x = labelWidthWest - fm.stringWidth(String.valueOf(value)) - - charWidth / 2; - g.drawString(value + "", x, - (ypos + (i * charHeight)) - (charHeight / 5)); - } - } - } - - /** - * DOCUMENT ME! - * - * @param g - * DOCUMENT ME! - * @param startx - * DOCUMENT ME! - * @param endx - * DOCUMENT ME! - * @param ypos - * DOCUMENT ME! - */ - void drawEastScale(Graphics g, int startx, int endx, int ypos) - { - ypos += charHeight; - - if (av.hasHiddenColumns()) - { - endx = av.getAlignment().getHiddenColumns() - .adjustForHiddenColumns(endx); - } - - SequenceI seq; - // EAST SCALE - for (int i = 0; i < av.getAlignment().getHeight(); i++) - { - seq = av.getAlignment().getSequenceAt(i); - int index = endx; - int value = -1; - - while (index > startx) - { - if (jalview.util.Comparison.isGap(seq.getCharAt(index))) + else { index--; - - continue; } - - value = seq.findPosition(index); - - break; } + /* + * white fill the space for the scale + */ + g.setColor(Color.white); + int y = (yPos + (i * charHeight)) - (charHeight / 5); + // fillRect origin is top left of rectangle + g.fillRect(0, y - charHeight, left ? labelWidthWest : labelWidthEast, + charHeight + 1); + if (value != -1) { - g.drawString(String.valueOf(value), 0, - (ypos + (i * charHeight)) - (charHeight / 5)); + /* + * draw scale value, right justified within its width less half a + * character width padding on the right + */ + int labelSpace = left ? labelWidthWest : labelWidthEast; + labelSpace -= charWidth / 2; // leave space to the right + String valueAsString = String.valueOf(value); + int labelLength = fm.stringWidth(valueAsString); + int xOffset = labelSpace - labelLength; + g.setColor(Color.black); + g.drawString(valueAsString, xOffset, y); } } } - /** - * need to make this thread safe move alignment rendering in response to - * slider adjustment + * Does a fast paint of an alignment in response to a scroll. Most of the + * visible region is simply copied and shifted, and then any newly visible + * columns or rows are drawn. The scroll may be horizontal or vertical, but + * not both at once. Scrolling may be the result of + *
      + *
    • dragging a scroll bar
    • + *
    • clicking in the scroll bar
    • + *
    • scrolling by trackpad, middle mouse button, or other device
    • + *
    • by moving the box in the Overview window
    • + *
    • programmatically to make a highlighted position visible
    • + *
    * * @param horizontal - * shift along + * columns to shift right (positive) or left (negative) * @param vertical - * shift up or down in repaint + * rows to shift down (positive) or up (negative) */ public void fastPaint(int horizontal, int vertical) { @@ -289,15 +282,19 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } fastpainting = true; fastPaint = true; - updateViewport(); - ViewportRanges ranges = av.getRanges(); - int startRes = ranges.getStartRes(); - int endRes = ranges.getEndRes(); - int startSeq = ranges.getStartSeq(); - int endSeq = ranges.getEndSeq(); - int transX = 0; - int transY = 0; + try + { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + + ViewportRanges ranges = av.getRanges(); + int startRes = ranges.getStartRes(); + int endRes = ranges.getEndRes(); + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + int transX = 0; + int transY = 0; gg.copyArea(horizontal * charWidth, vertical * charHeight, img.getWidth(), img.getHeight(), -horizontal * charWidth, @@ -340,15 +337,19 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.translate(-transX, -transY); repaint(); - fastpainting = false; + } finally + { + fastpainting = false; + } } @Override public void paintComponent(Graphics g) { - super.paintComponent(g); - - updateViewport(); + super.paintComponent(g); + + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); ViewportRanges ranges = av.getRanges(); @@ -411,7 +412,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.drawImage(lcimg, 0, 0, this); } } - + /** * Draw an alignment panel for printing * @@ -519,6 +520,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { BufferedImage lcimg = null; + int charWidth = av.getCharWidth(); + int charHeight = av.getCharHeight(); + int width = getWidth(); int height = getHeight(); @@ -558,29 +562,30 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ public int getWrappedCanvasWidth(int canvasWidth) { - FontMetrics fm = getFontMetrics(av.getFont()); + int charWidth = av.getCharWidth(); - labelWidthEast = 0; - labelWidthWest = 0; + FontMetrics fm = getFontMetrics(av.getFont()); - if (av.getScaleRightWrapped()) + int labelWidth = 0; + + if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) { - labelWidthEast = getLabelWidth(fm); + labelWidth = getLabelWidth(fm); } - if (av.getScaleLeftWrapped()) - { - labelWidthWest = labelWidthEast > 0 ? labelWidthEast - : getLabelWidth(fm); - } + labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; + + labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; } /** - * Returns a pixel width suitable for showing the largest sequence coordinate - * (end position) in the alignment. Returns 2 plus the number of decimal - * digits to be shown (3 for 1-10, 4 for 11-99 etc). + * Returns a pixel width sufficient to show the largest sequence coordinate + * (end position) in the alignment, calculated as the FontMetrics width of + * zeroes "0000000" limited to the number of decimal digits to be shown (3 for + * 1-10, 4 for 11-99 etc). One character width is added to this, to allow for + * half a character width space on either side. * * @param fm * @return @@ -598,160 +603,305 @@ public class SeqCanvas extends JComponent implements ViewportListenerI maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd()); } - int length = 2; + int length = 0; for (int i = maxWidth; i > 0; i /= 10) { length++; } - return fm.stringWidth(ZEROS.substring(0, length)); + return fm.stringWidth(ZEROS.substring(0, length)) + av.getCharWidth(); } /** - * DOCUMENT ME! + * Draws as many widths of a wrapped alignment as can fit in the visible + * window * * @param g - * DOCUMENT ME! * @param canvasWidth - * DOCUMENT ME! + * available width in pixels * @param canvasHeight - * DOCUMENT ME! - * @param startRes - * DOCUMENT ME! + * available height in pixels + * @param startColumn + * the first column (0...) of the alignment to draw */ - private void drawWrappedPanel(Graphics g, int canvasWidth, - int canvasHeight, int startRes) + public void drawWrappedPanel(Graphics g, int canvasWidth, + int canvasHeight, final int startColumn) { - updateViewport(); - AlignmentI al = av.getAlignment(); + int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth, + canvasHeight); - int labelWidth = 0; - if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) + av.setWrappedWidth(wrappedWidthInResidues); + + ViewportRanges ranges = av.getRanges(); + ranges.setViewportStartAndWidth(startColumn, wrappedWidthInResidues); + + /* + * draw one width at a time (including any scales or annotation shown), + * until we have run out of either alignment or vertical space available + */ + int ypos = wrappedSpaceAboveAlignment; + int maxWidth = ranges.getVisibleAlignmentWidth(); + + int start = startColumn; + int currentWidth = 0; + while ((currentWidth < wrappedVisibleWidths) && (start < maxWidth)) { - FontMetrics fm = getFontMetrics(av.getFont()); - labelWidth = getLabelWidth(fm); + int endColumn = Math + .min(maxWidth, start + wrappedWidthInResidues - 1); + drawWrappedWidth(g, ypos, start, endColumn, canvasHeight); + ypos += wrappedRepeatHeightPx; + start += wrappedWidthInResidues; + currentWidth++; } - labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; - labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; + drawWrappedDecorators(g, startColumn); + } - int hgap = charHeight; - if (av.getScaleAboveWrapped()) + /** + * Calculates and saves values needed when rendering a wrapped alignment. + * These depend on many factors, including + *
      + *
    • canvas width and height
    • + *
    • number of visible sequences, and height of annotations if shown
    • + *
    • font and character width
    • + *
    • whether scales are shown left, right or above the alignment
    • + *
    + * + * @param canvasWidth + * @param canvasHeight + * @return the number of residue columns in each width + */ + protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight) + { + int charHeight = av.getCharHeight(); + + /* + * vertical space in pixels between wrapped widths of alignment + * - one character height, or two if scale above is drawn + */ + wrappedSpaceAboveAlignment = charHeight + * (av.getScaleAboveWrapped() ? 2 : 1); + + /* + * height in pixels of the wrapped widths + */ + wrappedRepeatHeightPx = wrappedSpaceAboveAlignment; + // add sequences + wrappedRepeatHeightPx += av.getRanges().getViewportHeight() + * charHeight; + // add annotations panel height if shown + wrappedRepeatHeightPx += getAnnotationHeight(); + + /* + * number of visible widths (the last one may be part height), + * ensuring a part height includes at least one sequence + */ + ViewportRanges ranges = av.getRanges(); + wrappedVisibleWidths = canvasHeight / wrappedRepeatHeightPx; + int remainder = canvasHeight % wrappedRepeatHeightPx; + if (remainder >= (wrappedSpaceAboveAlignment + charHeight)) { - hgap += charHeight; + wrappedVisibleWidths++; } - int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; - int cHeight = av.getAlignment().getHeight() * charHeight; + /* + * compute width in residues; this also sets East and West label widths + */ + int wrappedWidthInResidues = getWrappedCanvasWidth(canvasWidth); - av.setWrappedWidth(cWidth); + /* + * limit visibleWidths to not exceed width of alignment + */ + int xMax = ranges.getVisibleAlignmentWidth(); + int startToEnd = xMax - ranges.getStartRes(); + int maxWidths = startToEnd / wrappedWidthInResidues; + if (startToEnd % wrappedWidthInResidues > 0) + { + maxWidths++; + } + wrappedVisibleWidths = Math.min(wrappedVisibleWidths, maxWidths); - av.getRanges().setViewportStartAndWidth(startRes, cWidth); + return wrappedWidthInResidues; + } - int endx; - int ypos = hgap; - int maxwidth = av.getAlignment().getWidth(); + /** + * Draws one width of a wrapped alignment, including sequences and + * annnotations, if shown, but not scales or hidden column markers + * + * @param g + * @param ypos + * @param startColumn + * @param endColumn + * @param canvasHeight + */ + protected void drawWrappedWidth(Graphics g, int ypos, int startColumn, + int endColumn, int canvasHeight) + { + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); - if (av.hasHiddenColumns()) + int endx = Math.min(startColumn + viewportWidth - 1, endColumn); + + /* + * move right before drawing by the width of the scale left (if any) + * plus column offset from left margin (usually zero, but may be non-zero + * when fast painting is drawing just a few columns) + */ + int charWidth = av.getCharWidth(); + int xOffset = labelWidthWest + + ((startColumn - ranges.getStartRes()) % viewportWidth) + * charWidth; + g.translate(xOffset, 0); + + // When printing we have an extra clipped region, + // the Printable page which we need to account for here + Shape clip = g.getClip(); + + if (clip == null) { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth); + g.setClip(0, 0, viewportWidth * charWidth, canvasHeight); + } + else + { + g.setClip(0, (int) clip.getBounds().getY(), + viewportWidth * charWidth, (int) clip.getBounds().getHeight()); } - int annotationHeight = getAnnotationHeight(); + /* + * white fill the region to be drawn (so incremental fast paint doesn't + * scribble over an existing image) + */ + gg.setColor(Color.white); + gg.fillRect(0, ypos, (endx - startColumn + 1) * charWidth, + wrappedRepeatHeightPx); - while ((ypos <= canvasHeight) && (startRes < maxwidth)) - { - endx = startRes + cWidth - 1; + drawPanel(g, startColumn, endx, 0, av.getAlignment().getHeight() - 1, + ypos); - if (endx > maxwidth) + int cHeight = av.getAlignment().getHeight() * av.getCharHeight(); + + if (av.isShowAnnotation()) + { + g.translate(0, cHeight + ypos + 3); + if (annotations == null) { - endx = maxwidth; + annotations = new AnnotationPanel(av); } - g.setFont(av.getFont()); - g.setColor(Color.black); + annotations.renderer.drawComponent(annotations, av, g, -1, + startColumn, endx + 1); + g.translate(0, -cHeight - ypos - 3); + } + g.setClip(clip); + g.translate(-xOffset, 0); + } + + /** + * Draws scales left, right and above (if shown), and any hidden column + * markers, on all widths of the wrapped alignment + * + * @param g + * @param startColumn + */ + protected void drawWrappedDecorators(Graphics g, final int startColumn) + { + int charWidth = av.getCharWidth(); + + g.setFont(av.getFont()); + g.setColor(Color.black); + + int ypos = wrappedSpaceAboveAlignment; + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); + int maxWidth = ranges.getVisibleAlignmentWidth(); + int widthsDrawn = 0; + int startCol = startColumn; + + while (widthsDrawn < wrappedVisibleWidths) + { + int endColumn = Math.min(maxWidth, startCol + viewportWidth - 1); if (av.getScaleLeftWrapped()) { - drawWestScale(g, startRes, endx, ypos); + drawVerticalScale(g, startCol, endColumn - 1, ypos, true); } if (av.getScaleRightWrapped()) { - g.translate(canvasWidth - labelWidthEast, 0); - drawEastScale(g, startRes, endx, ypos); - g.translate(-(canvasWidth - labelWidthEast), 0); + int x = labelWidthWest + viewportWidth * charWidth; + g.translate(x, 0); + drawVerticalScale(g, startCol, endColumn, ypos, false); + g.translate(-x, 0); } + /* + * white fill region of scale above and hidden column markers + * (to support incremental fast paint of image) + */ + g.translate(labelWidthWest, 0); + g.setColor(Color.white); + g.fillRect(0, ypos - wrappedSpaceAboveAlignment, viewportWidth + * charWidth + labelWidthWest, wrappedSpaceAboveAlignment); + g.setColor(Color.black); + g.translate(-labelWidthWest, 0); + g.translate(labelWidthWest, 0); if (av.getScaleAboveWrapped()) { - drawNorthScale(g, startRes, endx, ypos); + drawNorthScale(g, startCol, endColumn, ypos); } if (av.hasHiddenColumns() && av.getShowHiddenMarkers()) { - g.setColor(Color.blue); - int res; - HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - List positions = hidden.findHiddenRegionPositions(); - for (int pos : positions) - { - res = pos - startRes; - - if (res < 0 || res > endx - startRes) - { - continue; - } - - gg.fillPolygon( - new int[] - { res * charWidth - charHeight / 4, - res * charWidth + charHeight / 4, res * charWidth }, - new int[] - { ypos - (charHeight / 2), ypos - (charHeight / 2), - ypos - (charHeight / 2) + 8 }, - 3); - - } + drawHiddenColumnMarkers(g, ypos, startCol, endColumn); } - // When printing we have an extra clipped region, - // the Printable page which we need to account for here - Shape clip = g.getClip(); + g.translate(-labelWidthWest, 0); - if (clip == null) - { - g.setClip(0, 0, cWidth * charWidth, canvasHeight); - } - else - { - g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth, - (int) clip.getBounds().getHeight()); - } + ypos += wrappedRepeatHeightPx; + startCol += viewportWidth; + widthsDrawn++; + } + } + + /** + * Draws markers (triangles) above hidden column positions between startColumn + * and endColumn. + * + * @param g + * @param ypos + * @param startColumn + * @param endColumn + */ + protected void drawHiddenColumnMarkers(Graphics g, int ypos, + int startColumn, int endColumn) + { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); - drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos); + g.setColor(Color.blue); + HiddenColumns hidden = av.getAlignment().getHiddenColumns(); + List positions = hidden.findHiddenRegionPositions(); + for (int pos : positions) + { + int res = pos - startColumn; - if (av.isShowAnnotation()) + if (res < 0 || res > endColumn - startColumn + 1) { - g.translate(0, cHeight + ypos + 3); - if (annotations == null) - { - annotations = new AnnotationPanel(av); - } - - annotations.renderer.drawComponent(annotations, av, g, -1, startRes, - endx + 1); - g.translate(0, -cHeight - ypos - 3); + continue; } - g.setClip(clip); - g.translate(-labelWidthWest, 0); - - ypos += cHeight + annotationHeight + hgap; - startRes += cWidth; + /* + * draw a downward-pointing triangle at the hidden columns location + * (before the following visible column) + */ + int xMiddle = res * charWidth; + int[] xPoints = new int[] { xMiddle - charHeight / 4, + xMiddle + charHeight / 4, xMiddle }; + int yTop = ypos - (charHeight / 2); + int[] yPoints = new int[] { yTop, yTop, yTop + 8 }; + g.fillPolygon(xPoints, yPoints, 3); } } @@ -762,6 +912,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int canvasWidth, int canvasHeight, int startRes) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + // height gap above each panel int hgap = charHeight; if (av.getScaleAboveWrapped()) @@ -834,22 +987,24 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * marker. * * @param g1 - * Graphics object to draw with + * the graphics context, positioned at the first residue to be drawn * @param startRes - * offset of the first column in the visible region (0..) + * offset of the first column to draw (0..) * @param endRes - * offset of the last column in the visible region (0..) + * offset of the last column to draw (0..) * @param startSeq - * offset of the first sequence in the visible region (0..) + * offset of the first sequence to draw (0..) * @param endSeq - * offset of the last sequence in the visible region (0..) + * offset of the last sequence to draw (0..) * @param yOffset * vertical offset at which to draw (for wrapped alignments) */ public void drawPanel(Graphics g1, final int startRes, final int endRes, final int startSeq, final int endSeq, final int yOffset) { - updateViewport(); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + if (!av.hasHiddenColumns()) { draw(g1, startRes, endRes, startSeq, endSeq, yOffset); @@ -939,6 +1094,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void draw(Graphics g, int startRes, int endRes, int startSeq, int endSeq, int offset) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + g.setFont(av.getFont()); seqRdr.prepare(g, av.isRenderGaps()); @@ -1118,6 +1276,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, int offset) { + int charWidth = av.getCharWidth(); + if (!av.hasHiddenColumns()) { drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, @@ -1179,6 +1339,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int startRes, int endRes, int startSeq, int endSeq, int verticalOffset) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + int visWidth = (endRes - startRes + 1) * charWidth; int oldY = -1; @@ -1347,14 +1510,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return false; } boolean wrapped = av.getWrapAlignment(); - try { fastPaint = !noFastPaint; fastpainting = fastPaint; - updateViewport(); - /* * to avoid redrawing the whole visible region, we instead * redraw just the minimal regions to remove previous highlights @@ -1498,52 +1658,303 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { fastPaint = true; repaint(); + return; } - else if (av.getWrapAlignment()) + + int scrollX = 0; + if (eventName.equals(ViewportRanges.STARTRES)) { - if (eventName.equals(ViewportRanges.STARTRES)) + // Make sure we're not trying to draw a panel + // larger than the visible window + ViewportRanges vpRanges = av.getRanges(); + scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); + int range = vpRanges.getViewportWidth(); + if (scrollX > range) { - repaint(); + scrollX = range; + } + else if (scrollX < -range) + { + scrollX = -range; } } - else + + // Both scrolling and resizing change viewport ranges: scrolling changes + // both start and end points, but resize only changes end values. + // Here we only want to fastpaint on a scroll, with resize using a normal + // paint, so scroll events are identified as changes to the horizontal or + // vertical start value. + + // scroll - startres and endres both change + if (eventName.equals(ViewportRanges.STARTRES)) { - int scrollX = 0; - if (eventName.equals(ViewportRanges.STARTRES)) + if (av.getWrapAlignment()) { - // Make sure we're not trying to draw a panel - // larger than the visible window - ViewportRanges vpRanges = av.getRanges(); - scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); - int range = vpRanges.getEndRes() - vpRanges.getStartRes(); - if (scrollX > range) - { - scrollX = range; - } - else if (scrollX < -range) - { - scrollX = -range; - } + fastPaintWrapped(scrollX); } - - // Both scrolling and resizing change viewport ranges: scrolling changes - // both start and end points, but resize only changes end values. - // Here we only want to fastpaint on a scroll, with resize using a normal - // paint, so scroll events are identified as changes to the horizontal or - // vertical start value. - if (eventName.equals(ViewportRanges.STARTRES)) + else { - // scroll - startres and endres both change fastPaint(scrollX, 0); } - else if (eventName.equals(ViewportRanges.STARTSEQ)) + } + else if (eventName.equals(ViewportRanges.STARTSEQ)) + { + // scroll + fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); + } + } + + /** + * Does a minimal update of the image for a scroll movement. This method + * handles scroll movements of up to one width of the wrapped alignment (one + * click in the vertical scrollbar). Larger movements (for example after a + * scroll to highlight a mapped position) trigger a full redraw instead. + * + * @param scrollX + * number of positions scrolled (right if positive, left if negative) + */ + protected void fastPaintWrapped(int scrollX) + { + ViewportRanges ranges = av.getRanges(); + + if (Math.abs(scrollX) > ranges.getViewportWidth()) + { + /* + * shift of more than one view width is + * overcomplicated to handle in this method + */ + fastPaint = false; + repaint(); + return; + } + + if (fastpainting || gg == null) + { + return; + } + + fastPaint = true; + fastpainting = true; + + try + { + calculateWrappedGeometry(getWidth(), getHeight()); + + /* + * relocate the regions of the alignment that are still visible + */ + shiftWrappedAlignment(-scrollX); + + /* + * add new columns (sequence, annotation) + * - at top left if scrollX < 0 + * - at right of last two widths if scrollX > 0 + */ + if (scrollX < 0) + { + int startRes = ranges.getStartRes(); + drawWrappedWidth(gg, wrappedSpaceAboveAlignment, startRes, startRes + - scrollX - 1, getHeight()); + } + else + { + fastPaintWrappedAddRight(scrollX); + } + + /* + * draw all scales (if shown) and hidden column markers + */ + drawWrappedDecorators(gg, ranges.getStartRes()); + + repaint(); + } finally + { + fastpainting = false; + } + } + + /** + * Draws the specified number of columns at the 'end' (bottom right) of a + * wrapped alignment view, including sequences and annotations if shown, but + * not scales. Also draws the same number of columns at the right hand end of + * the second last width shown, if the last width is not full height (so + * cannot simply be copied from the graphics image). + * + * @param columns + */ + protected void fastPaintWrappedAddRight(int columns) + { + if (columns == 0) + { + return; + } + + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); + int charWidth = av.getCharWidth(); + + /** + * draw full height alignment in the second last row, last columns, if the + * last row was not full height + */ + int visibleWidths = wrappedVisibleWidths; + int canvasHeight = getHeight(); + boolean lastWidthPartHeight = (wrappedVisibleWidths * wrappedRepeatHeightPx) > canvasHeight; + + if (lastWidthPartHeight) + { + int widthsAbove = Math.max(0, visibleWidths - 2); + int ypos = wrappedRepeatHeightPx * widthsAbove + + wrappedSpaceAboveAlignment; + int endRes = ranges.getEndRes(); + endRes += widthsAbove * viewportWidth; + int startRes = endRes - columns; + int xOffset = ((startRes - ranges.getStartRes()) % viewportWidth) + * charWidth; + + /* + * white fill first to erase annotations + */ + gg.translate(xOffset, 0); + gg.setColor(Color.white); + gg.fillRect(labelWidthWest, ypos, + (endRes - startRes + 1) * charWidth, wrappedRepeatHeightPx); + gg.translate(-xOffset, 0); + + drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + } + + /* + * draw newly visible columns in last wrapped width (none if we + * have reached the end of the alignment) + * y-offset for drawing last width is height of widths above, + * plus one gap row + */ + int widthsAbove = visibleWidths - 1; + int ypos = wrappedRepeatHeightPx * widthsAbove + + wrappedSpaceAboveAlignment; + int endRes = ranges.getEndRes(); + endRes += widthsAbove * viewportWidth; + int startRes = endRes - columns + 1; + + /* + * white fill first to erase annotations + */ + int xOffset = ((startRes - ranges.getStartRes()) % viewportWidth) + * charWidth; + gg.translate(xOffset, 0); + gg.setColor(Color.white); + int width = viewportWidth * charWidth - xOffset; + gg.fillRect(labelWidthWest, ypos, width, wrappedRepeatHeightPx); + gg.translate(-xOffset, 0); + + gg.setFont(av.getFont()); + gg.setColor(Color.black); + + if (startRes < ranges.getVisibleAlignmentWidth()) + { + drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + } + + /* + * and finally, white fill any space below the visible alignment + */ + int heightBelow = canvasHeight - visibleWidths * wrappedRepeatHeightPx; + if (heightBelow > 0) + { + gg.setColor(Color.white); + gg.fillRect(0, canvasHeight - heightBelow, getWidth(), heightBelow); + } + } + + /** + * Shifts the visible alignment by the specified number of columns - left if + * negative, right if positive. Copies and moves sequences and annotations (if + * shown). Scales, hidden column markers and any newly visible columns must be + * drawn separately. + * + * @param positions + */ + protected void shiftWrappedAlignment(int positions) + { + if (positions == 0) + { + return; + } + int charWidth = av.getCharWidth(); + + int canvasHeight = getHeight(); + ViewportRanges ranges = av.getRanges(); + int viewportWidth = ranges.getViewportWidth(); + int widthToCopy = (ranges.getViewportWidth() - Math.abs(positions)) + * charWidth; + int heightToCopy = wrappedRepeatHeightPx - wrappedSpaceAboveAlignment; + int xMax = ranges.getVisibleAlignmentWidth(); + + if (positions > 0) + { + /* + * shift right (after scroll left) + * for each wrapped width (starting with the last), copy (width-positions) + * columns from the left margin to the right margin, and copy positions + * columns from the right margin of the row above (if any) to the + * left margin of the current row + */ + + /* + * get y-offset of last wrapped width, first row of sequences + */ + int y = canvasHeight / wrappedRepeatHeightPx * wrappedRepeatHeightPx; + y += wrappedSpaceAboveAlignment; + int copyFromLeftStart = labelWidthWest; + int copyFromRightStart = copyFromLeftStart + widthToCopy; + + while (y >= 0) + { + gg.copyArea(copyFromLeftStart, y, widthToCopy, heightToCopy, + positions * charWidth, 0); + if (y > 0) + { + gg.copyArea(copyFromRightStart, y - wrappedRepeatHeightPx, + positions * charWidth, heightToCopy, -widthToCopy, + wrappedRepeatHeightPx); + } + + y -= wrappedRepeatHeightPx; + } + } + else + { + /* + * shift left (after scroll right) + * for each wrapped width (starting with the first), copy (width-positions) + * columns from the right margin to the left margin, and copy positions + * columns from the left margin of the row below (if any) to the + * right margin of the current row + */ + int xpos = av.getRanges().getStartRes(); + int y = wrappedSpaceAboveAlignment; + int copyFromRightStart = labelWidthWest - positions * charWidth; + + while (y < canvasHeight) { - // scroll - fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); + gg.copyArea(copyFromRightStart, y, widthToCopy, heightToCopy, + positions * charWidth, 0); + if (y + wrappedRepeatHeightPx < canvasHeight - wrappedRepeatHeightPx + && (xpos + viewportWidth <= xMax)) + { + gg.copyArea(labelWidthWest, y + wrappedRepeatHeightPx, -positions + * charWidth, heightToCopy, widthToCopy, + -wrappedRepeatHeightPx); + } + + y += wrappedRepeatHeightPx; + xpos += viewportWidth; } } } + /** * Redraws any positions in the search results in the visible region of a * wrapped alignment. Any highlights are drawn depending on the search results @@ -1560,11 +1971,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return false; } - + int charHeight = av.getCharHeight(); + boolean matchFound = false; + calculateWrappedGeometry(getWidth(), getHeight()); int wrappedWidth = av.getWrappedWidth(); - int wrappedHeight = getRepeatHeightWrapped(); + int wrappedHeight = wrappedRepeatHeightPx; ViewportRanges ranges = av.getRanges(); int canvasHeight = getHeight(); @@ -1663,23 +2076,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } /** - * Answers the height in pixels of a repeating section of the wrapped - * alignment, including space above, scale above if shown, sequences, and - * annotation panel if shown + * Answers the width in pixels of the left scale labels (0 if not shown) * * @return */ - protected int getRepeatHeightWrapped() + int getLabelWidthWest() { - // gap (and maybe scale) above - int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); - - // add sequences - repeatHeight += av.getRanges().getViewportHeight() * charHeight; - - // add annotations panel height if shown - repeatHeight += getAnnotationHeight(); - - return repeatHeight; + return labelWidthWest; } } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index ad80a3e..282c810 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -214,8 +214,8 @@ public class SeqPanel extends JPanel + hgap + seqCanvas.getAnnotationHeight(); int y = evt.getY(); - y -= hgap; - x = Math.max(0, x - seqCanvas.labelWidthWest); + y = Math.max(0, y - hgap); + x = Math.max(0, x - seqCanvas.getLabelWidthWest()); int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth()); if (cwidth < 1) @@ -483,7 +483,7 @@ public class SeqPanel extends JPanel av.setSelectionGroup(sg); } - ap.paintAlignment(false); + ap.paintAlignment(false, false); av.sendSelection(); } @@ -851,11 +851,12 @@ public class SeqPanel extends JPanel /** * set when the current UI interaction has resulted in a change that requires - * overview shading to be recalculated. this could be changed to something - * more expressive that indicates what actually has changed, so selective - * redraws can be applied + * shading in overviews and structures to be recalculated. this could be + * changed to a something more expressive that indicates what actually has + * changed, so selective redraws can be applied (ie. only structures, only + * overview, etc) */ - private boolean needOverviewUpdate = false; // TODO: refactor to avcontroller + private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller /** * set if av.getSelectionGroup() refers to a group that is defined on the @@ -1056,7 +1057,7 @@ public class SeqPanel extends JPanel } if (newWidth > 0) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); if (copyChanges) { /* @@ -1655,7 +1656,7 @@ public class SeqPanel extends JPanel final int res = findColumn(evt); final int seq = findSeq(evt); oldSeq = seq; - needOverviewUpdate = false; + updateOverviewAndStructs = false; startWrapBlock = wrappedBlock; @@ -1809,7 +1810,7 @@ public class SeqPanel extends JPanel // always do this - annotation has own state // but defer colourscheme update until hidden sequences are passed in boolean vischange = stretchGroup.recalcConservation(true); - needOverviewUpdate |= vischange && av.isSelectionDefinedGroup() + updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup() && afterDrag; if (stretchGroup.cs != null) { @@ -1829,8 +1830,10 @@ public class SeqPanel extends JPanel } } PaintRefresher.Refresh(this, av.getSequenceSetId()); - ap.paintAlignment(needOverviewUpdate); - needOverviewUpdate = false; + // TODO: structure colours only need updating if stretchGroup used to or now + // does contain sequences with structure views + ap.paintAlignment(updateOverviewAndStructs, updateOverviewAndStructs); + updateOverviewAndStructs = false; changeEndRes = false; changeStartRes = false; stretchGroup = null; @@ -1884,7 +1887,7 @@ public class SeqPanel extends JPanel if (res > (stretchGroup.getStartRes() - 1)) { stretchGroup.setEndRes(res); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } else if (changeStartRes) @@ -1892,7 +1895,7 @@ public class SeqPanel extends JPanel if (res < (stretchGroup.getEndRes() + 1)) { stretchGroup.setStartRes(res); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } @@ -1926,7 +1929,7 @@ public class SeqPanel extends JPanel if (stretchGroup.getSequences(null).contains(nextSeq)) { stretchGroup.deleteSequence(seq, false); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } else { @@ -1936,7 +1939,7 @@ public class SeqPanel extends JPanel } stretchGroup.addSequence(nextSeq, false); - needOverviewUpdate |= av.isSelectionDefinedGroup(); + updateOverviewAndStructs |= av.isSelectionDefinedGroup(); } } diff --git a/src/jalview/gui/SliderPanel.java b/src/jalview/gui/SliderPanel.java index e6ec822..93a2457 100755 --- a/src/jalview/gui/SliderPanel.java +++ b/src/jalview/gui/SliderPanel.java @@ -127,7 +127,7 @@ public class SliderPanel extends GSliderPanel @Override public void mouseReleased(MouseEvent evt) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } }); diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index 3ba9947..c8854a7 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -83,18 +83,18 @@ public abstract class StructureViewerBase extends GStructureViewer /** * list of sequenceSet ids associated with the view */ - protected List _aps = new ArrayList(); + protected List _aps = new ArrayList<>(); /** * list of alignment panels to use for superposition */ - protected Vector _alignwith = new Vector(); + protected Vector _alignwith = new Vector<>(); /** * list of alignment panels that are used for colouring structures by aligned * sequences */ - protected Vector _colourwith = new Vector(); + protected Vector _colourwith = new Vector<>(); private String viewId = null; @@ -170,7 +170,7 @@ public abstract class StructureViewerBase extends GStructureViewer { if (_alignwith == null) { - _alignwith = new Vector(); + _alignwith = new Vector<>(); } if (_alignwith.size() == 0 && ap != null) { @@ -468,7 +468,9 @@ public abstract class StructureViewerBase extends GStructureViewer if (apanel.getSeqPanel().seqCanvas.fr != null) { apanel.getSeqPanel().seqCanvas.fr.featuresAdded(); - apanel.paintAlignment(true); + // note - we don't do a refresh for structure here because we do it + // explicitly for all panels later on + apanel.paintAlignment(true, false); } /* @@ -717,11 +719,11 @@ public abstract class StructureViewerBase extends GStructureViewer if (_colourwith == null) { - _colourwith = new Vector(); + _colourwith = new Vector<>(); } if (_alignwith == null) { - _alignwith = new Vector(); + _alignwith = new Vector<>(); } ViewSelectionMenu seqColourBy = new ViewSelectionMenu( @@ -888,7 +890,7 @@ public abstract class StructureViewerBase extends GStructureViewer binding.setColourBySequence(seqColour.isSelected()); if (_colourwith == null) { - _colourwith = new Vector(); + _colourwith = new Vector<>(); } if (binding.isColourBySequence()) { diff --git a/src/jalview/gui/TextColourChooser.java b/src/jalview/gui/TextColourChooser.java index 3986561..53e2dee 100644 --- a/src/jalview/gui/TextColourChooser.java +++ b/src/jalview/gui/TextColourChooser.java @@ -184,9 +184,9 @@ public class TextColourChooser */ protected void saveInitialSettings() { - groupColour1 = new HashMap(); - groupColour2 = new HashMap(); - groupThreshold = new HashMap(); + groupColour1 = new HashMap<>(); + groupColour2 = new HashMap<>(); + groupThreshold = new HashMap<>(); if (sg == null) { @@ -237,7 +237,7 @@ public class TextColourChooser sg.textColour = col; } - ap.paintAlignment(true); + ap.paintAlignment(false, false); } void colour2Changed(Color col) @@ -255,7 +255,7 @@ public class TextColourChooser sg.textColour2 = col; } - ap.paintAlignment(true); + ap.paintAlignment(false, false); } void thresholdChanged(int value) @@ -273,7 +273,7 @@ public class TextColourChooser sg.thresholdTextColour = value; } - ap.paintAlignment(true); + ap.paintAlignment(false, false); } void setGroupTextColour() diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index 5e14fce..9403057 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -493,7 +493,7 @@ public class TreePanel extends GTreePanel if (treeCanvas.applyToAllViews) { - final ArrayList commands = new ArrayList(); + final ArrayList commands = new ArrayList<>(); for (AlignmentPanel ap : PaintRefresher .getAssociatedPanels(av.getSequenceSetId())) { @@ -550,13 +550,14 @@ public class TreePanel extends GTreePanel public CommandI sortAlignmentIn(AlignmentPanel ap) { + // TODO: move to alignment view controller AlignmentViewport viewport = ap.av; SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray(); AlignmentSorter.sortByTree(viewport.getAlignment(), tree); CommandI undo; undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment()); - ap.paintAlignment(true); + ap.paintAlignment(true, false); return undo; } diff --git a/src/jalview/gui/UserDefinedColours.java b/src/jalview/gui/UserDefinedColours.java index 8b45c40..3290500 100755 --- a/src/jalview/gui/UserDefinedColours.java +++ b/src/jalview/gui/UserDefinedColours.java @@ -136,7 +136,7 @@ public class UserDefinedColours extends GUserDefinedColours UserDefinedColours() { super(); - selectedButtons = new ArrayList(); + selectedButtons = new ArrayList<>(); } void showFrame() @@ -163,7 +163,7 @@ public class UserDefinedColours extends GUserDefinedColours if (upperCaseButtons == null) { - upperCaseButtons = new ArrayList(); + upperCaseButtons = new ArrayList<>(); } for (int i = 0; i < 20; i++) @@ -194,7 +194,7 @@ public class UserDefinedColours extends GUserDefinedColours if (lowerCaseButtons == null) { - lowerCaseButtons = new ArrayList(); + lowerCaseButtons = new ArrayList<>(); } for (int i = 0; i < 20; i++) @@ -631,8 +631,8 @@ public class UserDefinedColours extends GUserDefinedColours @Override protected void loadbutton_actionPerformed() { - upperCaseButtons = new ArrayList(); - lowerCaseButtons = new ArrayList(); + upperCaseButtons = new ArrayList<>(); + lowerCaseButtons = new ArrayList<>(); JalviewFileChooser chooser = new JalviewFileChooser("jc", "Jalview User Colours"); @@ -876,7 +876,7 @@ public class UserDefinedColours extends GUserDefinedColours protected void cancelButton_actionPerformed() { ap.alignFrame.changeColour(oldColourScheme); - ap.paintAlignment(true); + ap.paintAlignment(true, true); try { diff --git a/src/jalview/io/WSWUBlastClient.java b/src/jalview/io/WSWUBlastClient.java index 6e7df71..d526a31 100755 --- a/src/jalview/io/WSWUBlastClient.java +++ b/src/jalview/io/WSWUBlastClient.java @@ -184,7 +184,7 @@ public class WSWUBlastClient } } } - ap.paintAlignment(true); + ap.paintAlignment(true, false); } diff --git a/src/jalview/renderer/ScaleRenderer.java b/src/jalview/renderer/ScaleRenderer.java index 9fec256..d92608c 100644 --- a/src/jalview/renderer/ScaleRenderer.java +++ b/src/jalview/renderer/ScaleRenderer.java @@ -34,12 +34,24 @@ import java.util.List; */ public class ScaleRenderer { + /** + * Represents one major or minor scale mark + */ public final class ScaleMark { + /** + * true for a major scale mark, false for minor + */ public final boolean major; + /** + * visible column position (0..) e.g. 19 + */ public final int column; + /** + * text (if any) to show e.g. "20" + */ public final String text; ScaleMark(boolean isMajor, int col, String txt) @@ -48,19 +60,27 @@ public class ScaleRenderer column = col; text = txt; } + + /** + * String representation for inspection when debugging only + */ + @Override + public String toString() + { + return String.format("%s:%d:%s", major ? "major" : "minor", column, + text); + } } /** - * calculate positions markers on the alignment ruler + * Calculates position markers on the alignment ruler * * @param av * @param startx - * left-most column in visible view + * left-most column in visible view (0..) * @param endx - * - right-most column in visible view - * @return List of ScaleMark holding boolean: true/false for major/minor mark, - * marker position in alignment column coords, a String to be rendered - * at the position (or null) + * - right-most column in visible view (0..) + * @return */ public List calculateMarks(AlignViewportI av, int startx, int endx) @@ -87,41 +107,40 @@ public class ScaleRenderer scalestartx += 5; } List marks = new ArrayList(); - String string; - int refN, iadj; // todo: add a 'reference origin column' to set column number relative to - for (int i = scalestartx; i < endx; i += 5) + for (int i = scalestartx; i <= endx; i += 5) { if (((i - refSp) % 10) == 0) { + String text; if (refSeq == null) { - iadj = av.getAlignment().getHiddenColumns() + int iadj = av.getAlignment().getHiddenColumns() .adjustForHiddenColumns(i - 1) + 1; - string = String.valueOf(iadj); + text = String.valueOf(iadj); } else { - iadj = av.getAlignment().getHiddenColumns() + int iadj = av.getAlignment().getHiddenColumns() .adjustForHiddenColumns(i - 1); - refN = refSeq.findPosition(iadj); + int refN = refSeq.findPosition(iadj); // TODO show bounds if position is a gap // - ie L--R -> "1L|2R" for // marker if (iadj < refStartI) { - string = String.valueOf(iadj - refStartI); + text = String.valueOf(iadj - refStartI); } else if (iadj > refEndI) { - string = "+" + String.valueOf(iadj - refEndI); + text = "+" + String.valueOf(iadj - refEndI); } else { - string = String.valueOf(refN) + refSeq.getCharAt(iadj); + text = String.valueOf(refN) + refSeq.getCharAt(iadj); } } - marks.add(new ScaleMark(true, i - startx - 1, string)); + marks.add(new ScaleMark(true, i - startx - 1, text)); } else { diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index f16522f..1f47da3 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -216,7 +216,8 @@ public class FeatureRenderer extends FeatureRendererModel return null; } - if (Comparison.isGap(seq.getCharAt(column))) + // column is 'base 1' but getCharAt is an array index (ie from 0) + if (Comparison.isGap(seq.getCharAt(column - 1))) { /* * returning null allows the colour scheme to provide gap colour diff --git a/src/jalview/schemes/RNAHelicesColourChooser.java b/src/jalview/schemes/RNAHelicesColourChooser.java index 15cb157..9809fa9 100644 --- a/src/jalview/schemes/RNAHelicesColourChooser.java +++ b/src/jalview/schemes/RNAHelicesColourChooser.java @@ -58,7 +58,7 @@ public class RNAHelicesColourChooser oldcs = av.getGlobalColourScheme(); if (av.getAlignment().getGroups() != null) { - oldgroupColours = new Hashtable(); + oldgroupColours = new Hashtable<>(); for (SequenceGroup sg : ap.getAlignment().getGroups()) { if (sg.getColourScheme() != null) @@ -71,7 +71,7 @@ public class RNAHelicesColourChooser this.ap = ap; adjusting = true; - Vector list = new Vector(); + Vector list = new Vector<>(); int index = 1; AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation(); if (anns != null) @@ -105,6 +105,6 @@ public class RNAHelicesColourChooser av.setGlobalColourScheme(rhc); - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 3702cd0..b260cab 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -33,7 +33,6 @@ import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.Annotation; -import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenColumns; import jalview.datamodel.HiddenSequences; @@ -1673,13 +1672,6 @@ public abstract class AlignmentViewport } @Override - public CigarArray getViewAsCigars(boolean selectedRegionOnly) - { - return new CigarArray(alignment, alignment.getHiddenColumns(), - (selectedRegionOnly ? selectionGroup : null)); - } - - @Override public jalview.datamodel.AlignmentView getAlignmentView( boolean selectedOnly) { diff --git a/src/jalview/workers/AnnotationWorker.java b/src/jalview/workers/AnnotationWorker.java index 8569039..8f37f15 100644 --- a/src/jalview/workers/AnnotationWorker.java +++ b/src/jalview/workers/AnnotationWorker.java @@ -51,7 +51,7 @@ class AnnotationWorker extends AlignCalcWorker AnnotationProviderI counter) { super(viewport, panel); - ourAnnots = new ArrayList(); + ourAnnots = new ArrayList<>(); this.counter = counter; calcMan.registerWorker(this); } @@ -121,7 +121,10 @@ class AnnotationWorker extends AlignCalcWorker if (ap != null) { ap.adjustAnnotationHeight(); - ap.paintAlignment(true); + // TODO: only need to update colour and geometry if panel height changes + // and view is coloured by annotation, and the annotation is actually + // changed! + ap.paintAlignment(true, true); } } diff --git a/src/jalview/workers/ColumnCounterSetWorker.java b/src/jalview/workers/ColumnCounterSetWorker.java index 24cb717..74695fe 100644 --- a/src/jalview/workers/ColumnCounterSetWorker.java +++ b/src/jalview/workers/ColumnCounterSetWorker.java @@ -57,7 +57,7 @@ class ColumnCounterSetWorker extends AlignCalcWorker AlignmentViewPanel panel, FeatureSetCounterI counter) { super(viewport, panel); - ourAnnots = new ArrayList(); + ourAnnots = new ArrayList<>(); this.counter = counter; calcMan.registerWorker(this); } @@ -116,7 +116,7 @@ class ColumnCounterSetWorker extends AlignCalcWorker { ap.adjustAnnotationHeight(); } - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/workers/ConsensusThread.java b/src/jalview/workers/ConsensusThread.java index 4242b2a..335529c 100644 --- a/src/jalview/workers/ConsensusThread.java +++ b/src/jalview/workers/ConsensusThread.java @@ -64,7 +64,7 @@ public class ConsensusThread extends AlignCalcWorker { if (ap != null) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); } Thread.sleep(200); } catch (Exception ex) @@ -93,7 +93,7 @@ public class ConsensusThread extends AlignCalcWorker if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } catch (OutOfMemoryError error) { diff --git a/src/jalview/workers/ConservationThread.java b/src/jalview/workers/ConservationThread.java index 571234c..54b0191 100644 --- a/src/jalview/workers/ConservationThread.java +++ b/src/jalview/workers/ConservationThread.java @@ -75,7 +75,7 @@ public class ConservationThread extends AlignCalcWorker abortAndDestroy(); return; } - List ourAnnot = new ArrayList(); + List ourAnnot = new ArrayList<>(); AlignmentI alignment = alignViewport.getAlignment(); conservation = alignViewport.getAlignmentConservationAnnotation(); quality = alignViewport.getAlignmentQualityAnnot(); @@ -123,7 +123,7 @@ public class ConservationThread extends AlignCalcWorker } if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/workers/StrucConsensusThread.java b/src/jalview/workers/StrucConsensusThread.java index 5ed2885..61ec3d0 100644 --- a/src/jalview/workers/StrucConsensusThread.java +++ b/src/jalview/workers/StrucConsensusThread.java @@ -139,7 +139,7 @@ public class StrucConsensusThread extends AlignCalcWorker calcMan.workerComplete(this); if (ap != null) { - ap.paintAlignment(true); + ap.paintAlignment(true, true); } } diff --git a/src/jalview/ws/DasSequenceFeatureFetcher.java b/src/jalview/ws/DasSequenceFeatureFetcher.java index 0a61dff..c661e2c 100644 --- a/src/jalview/ws/DasSequenceFeatureFetcher.java +++ b/src/jalview/ws/DasSequenceFeatureFetcher.java @@ -135,7 +135,7 @@ public class DasSequenceFeatureFetcher boolean useJDasMultiThread) { this.useJDASMultiThread = useJDasMultiThread; - this.selectedSources = new ArrayList(); + this.selectedSources = new ArrayList<>(); // filter both sequences and sources to eliminate duplicates for (jalviewSourceI src : selectedSources2) { @@ -316,17 +316,17 @@ public class DasSequenceFeatureFetcher FeaturesClientMultipleSources fc = new FeaturesClientMultipleSources(); fc.setConnProps(sourceRegistry.getSessionHandler()); // Now sending requests one at a time to each server - ArrayList srcobj = new ArrayList(); - ArrayList src = new ArrayList(); - List> ids = new ArrayList>(); - List> idobj = new ArrayList>(); - List> sqset = new ArrayList>(); + ArrayList srcobj = new ArrayList<>(); + ArrayList src = new ArrayList<>(); + List> ids = new ArrayList<>(); + List> idobj = new ArrayList<>(); + List> sqset = new ArrayList<>(); for (jalviewSourceI _sr : selectedSources) { - Map slist = new HashMap(); - List idob = new ArrayList(); - List qset = new ArrayList(); + Map slist = new HashMap<>(); + List idob = new ArrayList<>(); + List qset = new ArrayList<>(); for (SequenceI seq : sequences) { @@ -368,8 +368,8 @@ public class DasSequenceFeatureFetcher sqset.add(slist); } } - Map, Exception>> errors = new HashMap, Exception>>(); - Map, DasGFFAdapter>> results = new HashMap, DasGFFAdapter>>(); + Map, Exception>> errors = new HashMap<>(); + Map, DasGFFAdapter>> results = new HashMap<>(); if (!useJDASMultiThread) { Iterator sources = src.iterator(); @@ -390,7 +390,7 @@ public class DasSequenceFeatureFetcher if (ers == null) { results.put(source, - ers = new HashMap, DasGFFAdapter>()); + ers = new HashMap<>()); } ers.put(qid, dga); } catch (Exception ex) @@ -399,7 +399,7 @@ public class DasSequenceFeatureFetcher if (ers == null) { errors.put(source, - ers = new HashMap, Exception>()); + ers = new HashMap<>()); } ers.put(qid, ex); } @@ -438,7 +438,7 @@ public class DasSequenceFeatureFetcher Map, DasGFFAdapter> results, Map, Exception> errors) { - Set sequences = new HashSet(); + Set sequences = new HashSet<>(); String source = jvsource.getSourceURL(); // process features DasGFFAdapter result = (results == null) ? null : results.get(ids); @@ -622,7 +622,7 @@ public class DasSequenceFeatureFetcher if (seq == af.getViewport().getAlignment().getSequenceAt(index) .getDatasetSequence()) { - af.alignPanel.paintAlignment(true); + af.alignPanel.paintAlignment(true, true); index = end; break; } @@ -647,8 +647,8 @@ public class DasSequenceFeatureFetcher // TODO: minimal list of DAS queries to make by querying with untyped ID if // distinct from any typed IDs - List ids = new ArrayList(); - List qstring = new ArrayList(); + List ids = new ArrayList<>(); + List qstring = new ArrayList<>(); boolean dasCoordSysFound = false; if (uprefs != null) diff --git a/src/jalview/ws/jws2/AADisorderClient.java b/src/jalview/ws/jws2/AADisorderClient.java index c24ea05..9a2316c 100644 --- a/src/jalview/ws/jws2/AADisorderClient.java +++ b/src/jalview/ws/jws2/AADisorderClient.java @@ -99,22 +99,22 @@ public class AADisorderClient extends JabawsCalcWorker { // TODO: turn this into some kind of configuration file that's a bit easier // to edit - featureMap = new HashMap>(); + featureMap = new HashMap<>(); Map fmap; featureMap.put(compbio.ws.client.Services.IUPredWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); fmap.put("Glob", new String[] { "Globular Domain", "Predicted globular domain" }); featureMap.put(compbio.ws.client.Services.JronnWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); featureMap.put(compbio.ws.client.Services.DisemblWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); fmap.put("REM465", new String[] { "REM465", "Missing density" }); fmap.put("HOTLOOPS", new String[] { "HOTLOOPS", "Flexible loops" }); fmap.put("COILS", new String[] { "COILS", "Random coil" }); featureMap.put(compbio.ws.client.Services.GlobPlotWS.toString(), - fmap = new HashMap()); + fmap = new HashMap<>()); fmap.put("GlobDoms", new String[] { "Globular Domain", "Predicted globular domain" }); @@ -122,9 +122,9 @@ public class AADisorderClient extends JabawsCalcWorker new String[] { "Protein Disorder", "Probable unstructured peptide region" }); Map> amap; - annotMap = new HashMap>>(); + annotMap = new HashMap<>(); annotMap.put(compbio.ws.client.Services.GlobPlotWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("Dydx", new HashMap()); amap.get("Dydx").put(DONTCOMBINE, DONTCOMBINE); amap.get("Dydx").put(THRESHOLD, new double[] { 1, 0 }); @@ -135,7 +135,7 @@ public class AADisorderClient extends JabawsCalcWorker amap.put("RawScore", new HashMap()); amap.get("RawScore").put(INVISIBLE, INVISIBLE); annotMap.put(compbio.ws.client.Services.DisemblWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("COILS", new HashMap()); amap.put("HOTLOOPS", new HashMap()); amap.put("REM465", new HashMap()); @@ -148,7 +148,7 @@ public class AADisorderClient extends JabawsCalcWorker amap.get("REM465").put(RANGE, new float[] { 0, 1 }); annotMap.put(compbio.ws.client.Services.IUPredWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("Long", new HashMap()); amap.put("Short", new HashMap()); amap.get("Long").put(THRESHOLD, new double[] { 1, 0.5 }); @@ -156,7 +156,7 @@ public class AADisorderClient extends JabawsCalcWorker amap.get("Short").put(THRESHOLD, new double[] { 1, 0.5 }); amap.get("Short").put(RANGE, new float[] { 0, 1 }); annotMap.put(compbio.ws.client.Services.JronnWS.toString(), - amap = new HashMap>()); + amap = new HashMap<>()); amap.put("JRonn", new HashMap()); amap.get("JRonn").put(THRESHOLD, new double[] { 1, 0.5 }); amap.get("JRonn").put(RANGE, new float[] { 0, 1 }); @@ -173,8 +173,8 @@ public class AADisorderClient extends JabawsCalcWorker Map> annotTypeMap = annotMap .get(service.serviceType); boolean dispFeatures = false; - Map fc = new Hashtable(); - List ourAnnot = new ArrayList(); + Map fc = new Hashtable<>(); + List ourAnnot = new ArrayList<>(); /** * grouping for any annotation rows created */ @@ -358,7 +358,6 @@ public class AADisorderClient extends JabawsCalcWorker // only do this if the alignFrame is currently showing this view. af.setShowSeqFeatures(true); } - ap.paintAlignment(true); } if (ourAnnot.size() > 0) { @@ -366,6 +365,7 @@ public class AADisorderClient extends JabawsCalcWorker // new alignment annotation rows created. updateOurAnnots(ourAnnot); ap.adjustAnnotationHeight(); + ap.paintAlignment(true, true); } } } diff --git a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java index 26fe0a2..b972ab8 100644 --- a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java +++ b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java @@ -372,7 +372,8 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker { guiProgress.setProgressBar("", progressId); } - ap.paintAlignment(true); + // TODO: may not need to paintAlignment again ! + ap.paintAlignment(false, false); } if (msg.length() > 0) { @@ -582,7 +583,7 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker protected boolean checkDone() { calcMan.notifyStart(this); - ap.paintAlignment(false); + ap.paintAlignment(false, false); while (!calcMan.notifyWorking(this)) { if (calcMan.isWorking(this)) @@ -593,7 +594,7 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker { if (ap != null) { - ap.paintAlignment(false); + ap.paintAlignment(false, false); } Thread.sleep(200); diff --git a/test/jalview/datamodel/CigarArrayTest.java b/test/jalview/datamodel/CigarArrayTest.java new file mode 100644 index 0000000..7bee423 --- /dev/null +++ b/test/jalview/datamodel/CigarArrayTest.java @@ -0,0 +1,141 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.datamodel; + +import static org.testng.Assert.assertEquals; + +import jalview.gui.JvOptionPane; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class CigarArrayTest +{ + @BeforeClass(alwaysRun = true) + public void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + + @Test(groups = "Functional") + public void testConstructor() + { + SequenceI seq1 = new Sequence("sq1", + "ASFDDABACBACBACBACBACBACBABCABCBACBABCAB"); + Sequence seq2 = new Sequence("sq2", + "TTTTTTACBCBABCABCABCABCBACBACBABCABCABCBA"); + + // construct alignment + AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 }); + + // hide columns + HiddenColumns hc = new HiddenColumns(); + hc.hideColumns(3, 6); + hc.hideColumns(16, 20); + + // select group + SequenceGroup sg1 = new SequenceGroup(); + sg1.addSequence(seq1, false); + sg1.setStartRes(2); + sg1.setEndRes(23); + + // Cigar array meanings: + // M = match + // D = deletion + // I = insertion + // number preceding M/D/I is the number of residues which + // match/are deleted/are inserted + // In the CigarArray constructor only matches or deletions are created, as + // we are comparing a sequence to its own subsequence (the group) + hidden + // columns. + + // no hidden columns case + CigarArray cig = new CigarArray(al, null, sg1); + String result = cig.getCigarstring(); + assertEquals(result, "22M"); + + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "1M4D9M5D3M"); + + // group starts at hidden cols + sg1.setStartRes(3); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "4D9M5D3M"); + + // group starts at last but 1 hidden col + sg1.setStartRes(5); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D3M"); + + // group starts at last hidden col + sg1.setStartRes(6); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "1D9M5D3M"); + + // group starts just after hidden region + sg1.setStartRes(7); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "9M5D3M"); + + // group ends just before start of hidden region + sg1.setStartRes(5); + sg1.setEndRes(15); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M"); + + // group ends at start of hidden region + sg1.setEndRes(16); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M1D"); + + // group ends 1 after start of hidden region + sg1.setEndRes(17); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M2D"); + + // group ends at end of hidden region + sg1.setEndRes(20); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D"); + + // group ends just after end of hidden region + sg1.setEndRes(21); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D1M"); + + // group ends 2 after end of hidden region + sg1.setEndRes(22); + cig = new CigarArray(al, hc, sg1); + result = cig.getCigarstring(); + assertEquals(result, "2D9M5D2M"); + } +} diff --git a/test/jalview/datamodel/SequenceTest.java b/test/jalview/datamodel/SequenceTest.java index 6844072..c0cb09c 100644 --- a/test/jalview/datamodel/SequenceTest.java +++ b/test/jalview/datamodel/SequenceTest.java @@ -1672,4 +1672,135 @@ public class SequenceTest assertEquals(new Range(8, 13), sq.findPositions(1, 13)); // the lot assertEquals(new Range(8, 13), sq.findPositions(1, 99)); } + + @Test(groups = { "Functional" }) + public void testFindFeatures_largeEndPos() + { + /* + * imitate a PDB sequence where end is larger than end position + */ + SequenceI sq = new Sequence("test", "-ABC--DEF--", 1, 20); + sq.createDatasetSequence(); + + assertTrue(sq.findFeatures(1, 9).isEmpty()); + // should be no array bounds exception - JAL-2772 + assertTrue(sq.findFeatures(1, 15).isEmpty()); + + // add feature on BCD + SequenceFeature sfBCD = new SequenceFeature("Cath", "desc", 2, 4, 2f, + null); + sq.addSequenceFeature(sfBCD); + + // no features in columns 1-2 (-A) + List found = sq.findFeatures(1, 2); + assertTrue(found.isEmpty()); + + // columns 1-6 (-ABC--) includes BCD + found = sq.findFeatures(1, 6); + assertEquals(1, found.size()); + assertTrue(found.contains(sfBCD)); + + // columns 10-11 (--) should find nothing + found = sq.findFeatures(10, 11); + assertEquals(0, found.size()); + } + + @Test(groups = { "Functional" }) + public void testSetName() + { + SequenceI sq = new Sequence("test", "-ABC---DE-F--"); + assertEquals("test", sq.getName()); + assertEquals(1, sq.getStart()); + assertEquals(6, sq.getEnd()); + + sq.setName("testing"); + assertEquals("testing", sq.getName()); + + sq.setName("test/8-10"); + assertEquals("test", sq.getName()); + assertEquals(8, sq.getStart()); + assertEquals(13, sq.getEnd()); // note end is recomputed + + sq.setName("testing/7-99"); + assertEquals("testing", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); // end may be beyond physical end + + sq.setName("/2-3"); + assertEquals("", sq.getName()); + assertEquals(2, sq.getStart()); + assertEquals(7, sq.getEnd()); + + sq.setName("test/"); // invalid + assertEquals("test/", sq.getName()); + assertEquals(2, sq.getStart()); + assertEquals(7, sq.getEnd()); + + sq.setName("test/6-13/7-99"); + assertEquals("test/6-13", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/0-5"); // 0 is invalid - ignored + assertEquals("test/0-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/a-5"); // a is invalid - ignored + assertEquals("test/a-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/6-5"); // start > end is invalid - ignored + assertEquals("test/6-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/5"); // invalid - ignored + assertEquals("test/5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/-5"); // invalid - ignored + assertEquals("test/-5", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/5-"); // invalid - ignored + assertEquals("test/5-", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName("test/5-6-7"); // invalid - ignored + assertEquals("test/5-6-7", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + + sq.setName(null); // invalid, gets converted to space + assertEquals("", sq.getName()); + assertEquals(7, sq.getStart()); + assertEquals(99, sq.getEnd()); + } + + @Test(groups = { "Functional" }) + public void testCheckValidRange() + { + Sequence sq = new Sequence("test/7-12", "-ABC---DE-F--"); + assertEquals(7, sq.getStart()); + assertEquals(12, sq.getEnd()); + + /* + * checkValidRange ensures end is at least the last residue position + */ + PA.setValue(sq, "end", 2); + sq.checkValidRange(); + assertEquals(12, sq.getEnd()); + + /* + * end may be beyond the last residue position + */ + PA.setValue(sq, "end", 22); + sq.checkValidRange(); + assertEquals(22, sq.getEnd()); + } } diff --git a/test/jalview/ext/ensembl/EnsemblGeneTest.java b/test/jalview/ext/ensembl/EnsemblGeneTest.java index a8c491c..5920b89 100644 --- a/test/jalview/ext/ensembl/EnsemblGeneTest.java +++ b/test/jalview/ext/ensembl/EnsemblGeneTest.java @@ -296,4 +296,28 @@ public class EnsemblGeneTest assertEquals(-1, fc.compare("coding_exon", "feature_variant")); assertEquals(1f, fc.getTransparency()); } + + @Test(groups = "Network") + public void testGetGeneIds() + { + /* + * ENSG00000158828 gene id PINK1 human + * ENST00000321556 transcript for the same gene - should not be duplicated + * P30419 Uniprot identifier for ENSG00000136448 + * ENST00000592782 transcript for Uniprot gene - should not be duplicated + * BRAF - gene name resolvabe (at time of writing) for 6 model species + */ + String ids = "ENSG00000158828 ENST00000321556 P30419 ENST00000592782 BRAF"; + EnsemblGene testee = new EnsemblGene(); + List geneIds = testee.getGeneIds(ids); + assertEquals(8, geneIds.size()); + assertTrue(geneIds.contains("ENSG00000158828")); + assertTrue(geneIds.contains("ENSG00000136448")); + assertTrue(geneIds.contains("ENSG00000157764")); // BRAF human + assertTrue(geneIds.contains("ENSMUSG00000002413")); // mouse + assertTrue(geneIds.contains("ENSRNOG00000010957")); // rat + assertTrue(geneIds.contains("ENSXETG00000004845")); // xenopus + assertTrue(geneIds.contains("ENSDARG00000017661")); // zebrafish + assertTrue(geneIds.contains("ENSGALG00000012865")); // chicken + } } diff --git a/test/jalview/ext/ensembl/EnsemblRestClientTest.java b/test/jalview/ext/ensembl/EnsemblRestClientTest.java index 31001da..cc3a3db 100644 --- a/test/jalview/ext/ensembl/EnsemblRestClientTest.java +++ b/test/jalview/ext/ensembl/EnsemblRestClientTest.java @@ -20,21 +20,40 @@ */ package jalview.ext.ensembl; +import static org.testng.Assert.assertTrue; + import jalview.datamodel.AlignmentI; import java.net.MalformedURLException; import java.net.URL; import java.util.List; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class EnsemblRestClientTest { + private EnsemblRestClient sf; @Test(suiteName = "live") public void testIsEnsemblAvailable() { - EnsemblRestClient sf = new EnsemblRestClient() + boolean isAvailable = sf.isEnsemblAvailable(); + if (isAvailable) + { + System.out.println("Ensembl is UP!"); + } + else + { + System.err + .println("Ensembl is DOWN or unreachable ******************* BAD!"); + } + } + + @BeforeMethod(alwaysRun = true) + protected void setUp() + { + sf = new EnsemblRestClient() { @Override @@ -74,16 +93,14 @@ public class EnsemblRestClientTest } }; - boolean isAvailable = sf.isEnsemblAvailable(); - if (isAvailable) - { - System.out.println("Ensembl is UP!"); - } - else + } + + @Test(groups = "Network") + public void testCheckEnsembl_overload() + { + for (int i = 0; i < 20; i++) { - System.err - .println("Ensembl is DOWN or unreachable ******************* BAD!"); + assertTrue(sf.checkEnsembl(), "Error on " + (i + 1) + "th ping"); } } - } diff --git a/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java b/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java index aa2c315..e2af26b 100644 --- a/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java +++ b/test/jalview/ext/ensembl/EnsemblSeqProxyTest.java @@ -191,34 +191,6 @@ public class EnsemblSeqProxyTest } - @Test(groups = "Functional") - public void testIsTranscriptIdentifier() - { - EnsemblSeqProxy testee = new EnsemblGene(); - assertFalse(testee.isTranscriptIdentifier(null)); - assertFalse(testee.isTranscriptIdentifier("")); - assertFalse(testee.isTranscriptIdentifier("ENSG00000012345")); - assertTrue(testee.isTranscriptIdentifier("ENST00000012345")); - assertTrue(testee.isTranscriptIdentifier("ENSMUST00000012345")); - assertFalse(testee.isTranscriptIdentifier("enst00000012345")); - assertFalse(testee.isTranscriptIdentifier("ENST000000123456")); - assertFalse(testee.isTranscriptIdentifier("ENST0000001234")); - } - - @Test(groups = "Functional") - public void testIsGeneIdentifier() - { - EnsemblSeqProxy testee = new EnsemblGene(); - assertFalse(testee.isGeneIdentifier(null)); - assertFalse(testee.isGeneIdentifier("")); - assertFalse(testee.isGeneIdentifier("ENST00000012345")); - assertTrue(testee.isGeneIdentifier("ENSG00000012345")); - assertTrue(testee.isGeneIdentifier("ENSMUSG00000012345")); - assertFalse(testee.isGeneIdentifier("ensg00000012345")); - assertFalse(testee.isGeneIdentifier("ENSG000000123456")); - assertFalse(testee.isGeneIdentifier("ENSG0000001234")); - } - /** * Test the method that appends a single allele's reverse complement to a * string buffer diff --git a/test/jalview/ext/ensembl/SpeciesTest.java b/test/jalview/ext/ensembl/SpeciesTest.java new file mode 100644 index 0000000..44658e7 --- /dev/null +++ b/test/jalview/ext/ensembl/SpeciesTest.java @@ -0,0 +1,30 @@ +package jalview.ext.ensembl; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import org.testng.annotations.Test; + +public class SpeciesTest +{ + @Test + public void testGetModelOrganisms() + { + Set models = Species.getModelOrganisms(); + assertTrue(models.contains(Species.human)); + assertFalse(models.contains(Species.horse)); + for (Species s : Species.values()) + { + if (s.isModelOrganism()) + { + assertTrue(models.contains(s)); + } + else + { + assertFalse(models.contains(s)); + } + } + } +} diff --git a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java index c9e1cad..63d5e4e 100644 --- a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java +++ b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java @@ -29,6 +29,11 @@ public class AtomSpecModelTest assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); model.addRange(0, 3, 10, "C"); // subsumes 5-9 assertEquals(model.getAtomSpec(), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B"); + model.addRange(5, 25, 35, " "); // empty chain code - e.g. from homology + // modelling + assertEquals(model.getAtomSpec(), + "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35."); + } } diff --git a/test/jalview/gui/AlignmentPanelTest.java b/test/jalview/gui/AlignmentPanelTest.java index b228ba1..2819dbf 100644 --- a/test/jalview/gui/AlignmentPanelTest.java +++ b/test/jalview/gui/AlignmentPanelTest.java @@ -21,6 +21,7 @@ package jalview.gui; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; import jalview.bin.Cache; import jalview.bin.Jalview; @@ -218,4 +219,31 @@ public class AlignmentPanelTest .getAlignment().getWidth() - 1 - 21); // 21 is the number of hidden // columns } + + /** + * Test that update layout reverts to original (unwrapped) values for endRes + * and endSeq when switching from wrapped to unwrapped mode (JAL-2739) + */ + @Test(groups = "Functional") + public void TestUpdateLayout_endRes() + { + // get details of original alignment dimensions + ViewportRanges ranges = af.getViewport().getRanges(); + int endres = ranges.getEndRes(); + + // wrap + af.alignPanel.getAlignViewport().setWrapAlignment(true); + af.alignPanel.updateLayout(); + + // endRes changes + assertNotEquals(ranges.getEndRes(), endres); + + // unwrap + af.alignPanel.getAlignViewport().setWrapAlignment(false); + af.alignPanel.updateLayout(); + + // endRes and endSeq back to original values + assertEquals(ranges.getEndRes(), endres); + + } } diff --git a/test/jalview/gui/SeqCanvasTest.java b/test/jalview/gui/SeqCanvasTest.java new file mode 100644 index 0000000..05b9aea --- /dev/null +++ b/test/jalview/gui/SeqCanvasTest.java @@ -0,0 +1,281 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; + +import jalview.datamodel.AlignmentI; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; + +import java.awt.Font; +import java.awt.FontMetrics; + +import junit.extensions.PA; + +import org.testng.annotations.Test; + +public class SeqCanvasTest +{ + /** + * Test the method that computes wrapped width in residues, height of wrapped + * widths in pixels, and the number of widths visible + */ + @Test(groups = "Functional") + public void testCalculateWrappedGeometry_noAnnotations() + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/uniref50.fa", DataSourceType.FILE); + AlignViewport av = af.getViewport(); + AlignmentI al = av.getAlignment(); + assertEquals(al.getWidth(), 157); + assertEquals(al.getHeight(), 15); + + av.setWrapAlignment(true); + av.getRanges().setStartEndSeq(0, 14); + av.setFont(new Font("SansSerif", Font.PLAIN, 14), true); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + assertEquals(charHeight, 17); + assertEquals(charWidth, 12); + + SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas; + + /* + * first with scales above, left, right + */ + av.setShowAnnotation(false); + av.setScaleAboveWrapped(true); + av.setScaleLeftWrapped(true); + av.setScaleRightWrapped(true); + FontMetrics fm = testee.getFontMetrics(av.getFont()); + int labelWidth = fm.stringWidth("000") + charWidth; + assertEquals(labelWidth, 39); // 3 x 9 + charWidth + + /* + * width 400 pixels leaves (400 - 2*labelWidth) for residue columns + * take the whole multiple of character widths + */ + int canvasWidth = 400; + int canvasHeight = 300; + int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth; + int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(wrappedWidth, residueColumns); + assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth); + assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth); + assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"), + 2 * charHeight); + int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx"); + assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + + /* + * repeat height is 17 * (2 + 15) = 289 + * make canvas height 2 * 289 + 3 * charHeight so just enough to + * draw 2 widths and the first sequence of a third + */ + canvasHeight = charHeight * (17 * 2 + 3); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce canvas height by 1 pixel - should not be enough height + * to draw 3 widths + */ + canvasHeight -= 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * turn off scale above - can now fit in 2 and a bit widths + */ + av.setScaleAboveWrapped(false); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce height to enough for 2 widths and not quite a third + * i.e. two repeating heights + spacer + sequence - 1 pixel + */ + canvasHeight = charHeight * (16 * 2 + 2) - 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * make canvas width enough for scales and 20 residues + */ + canvasWidth = 2 * labelWidth + 20 * charWidth; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 20); + + /* + * reduce width by 1 pixel - rounds down to 19 residues + */ + canvasWidth -= 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 19); + + /* + * turn off West scale - adds labelWidth (39) to available for residues + * which with the 11 remainder makes 50 which is 4 more charWidths rem 2 + */ + av.setScaleLeftWrapped(false); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 23); + + /* + * add 10 pixels to width to fit in another whole residue column + */ + canvasWidth += 9; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 23); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + + /* + * turn off East scale to gain 39 more pixels (3 columns remainder 3) + */ + av.setScaleRightWrapped(false); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 27); + + /* + * add 9 pixels to width to gain a residue column + */ + canvasWidth += 8; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 27); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 28); + + /* + * now West but not East scale - lose 39 pixels or 4 columns + */ + av.setScaleLeftWrapped(true); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + + /* + * adding 3 pixels to width regains one column + */ + canvasWidth += 2; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 25); + + /* + * turn off scales left and right, make width exactly 157 columns + */ + av.setScaleLeftWrapped(false); + canvasWidth = al.getWidth() * charWidth; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + } + + /** + * Test the method that computes wrapped width in residues, height of wrapped + * widths in pixels, and the number of widths visible + */ + @Test(groups = "Functional") + public void testCalculateWrappedGeometry_withAnnotations() + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/uniref50.fa", DataSourceType.FILE); + AlignViewport av = af.getViewport(); + AlignmentI al = av.getAlignment(); + assertEquals(al.getWidth(), 157); + assertEquals(al.getHeight(), 15); + + av.setWrapAlignment(true); + av.getRanges().setStartEndSeq(0, 14); + av.setFont(new Font("SansSerif", Font.PLAIN, 14), true); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + assertEquals(charHeight, 17); + assertEquals(charWidth, 12); + + SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas; + + /* + * first with scales above, left, right + */ + av.setShowAnnotation(true); + av.setScaleAboveWrapped(true); + av.setScaleLeftWrapped(true); + av.setScaleRightWrapped(true); + FontMetrics fm = testee.getFontMetrics(av.getFont()); + int labelWidth = fm.stringWidth("000") + charWidth; + assertEquals(labelWidth, 39); // 3 x 9 + charWidth + int annotationHeight = testee.getAnnotationHeight(); + + /* + * width 400 pixels leaves (400 - 2*labelWidth) for residue columns + * take the whole multiple of character widths + */ + int canvasWidth = 400; + int canvasHeight = 300; + int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth; + int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(wrappedWidth, residueColumns); + assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth); + assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth); + assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"), + 2 * charHeight); + int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx"); + assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()) + + annotationHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + + /* + * repeat height is 17 * (2 + 15) = 289 + annotationHeight = 507 + * make canvas height 2 * 289 + 3 * charHeight so just enough to + * draw 2 widths and the first sequence of a third + */ + canvasHeight = charHeight * (17 * 2 + 3) + 2 * annotationHeight; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce canvas height by 1 pixel - should not be enough height + * to draw 3 widths + */ + canvasHeight -= 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * turn off scale above - can now fit in 2 and a bit widths + */ + av.setScaleAboveWrapped(false); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce height to enough for 2 widths and not quite a third + * i.e. two repeating heights + spacer + sequence - 1 pixel + */ + canvasHeight = charHeight * (16 * 2 + 2) + 2 * annotationHeight - 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * add 1 pixel to height - should now get 3 widths drawn + */ + canvasHeight += 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + } +} diff --git a/test/jalview/renderer/ScaleRendererTest.java b/test/jalview/renderer/ScaleRendererTest.java index cf1039f..0af67cd 100644 --- a/test/jalview/renderer/ScaleRendererTest.java +++ b/test/jalview/renderer/ScaleRendererTest.java @@ -26,11 +26,11 @@ public class ScaleRendererTest AlignViewport av = af.getViewport(); /* - * scale has minor ticks at 5 and 15, major at 10 and 20 + * scale has minor ticks at 5, 15, 25, major at 10 and 20 * (these are base 1, ScaleMark holds base 0 values) */ List marks = new ScaleRenderer().calculateMarks(av, 0, 25); - assertEquals(marks.size(), 4); + assertEquals(marks.size(), 5); assertFalse(marks.get(0).major); assertEquals(marks.get(0).column, 4); @@ -48,6 +48,10 @@ public class ScaleRendererTest assertEquals(marks.get(3).column, 19); assertEquals(marks.get(3).text, "20"); + assertFalse(marks.get(4).major); + assertEquals(marks.get(4).column, 24); + assertNull(marks.get(4).text); + /* * now hide columns 9-11 and 18-20 (base 1) * scale marks are now in the same columns as before, but @@ -56,7 +60,7 @@ public class ScaleRendererTest av.hideColumns(8, 10); av.hideColumns(17, 19); marks = new ScaleRenderer().calculateMarks(av, 0, 25); - assertEquals(marks.size(), 4); + assertEquals(marks.size(), 5); assertFalse(marks.get(0).major); assertEquals(marks.get(0).column, 4); assertNull(marks.get(0).text); @@ -69,5 +73,8 @@ public class ScaleRendererTest assertTrue(marks.get(3).major); assertEquals(marks.get(3).column, 19); assertEquals(marks.get(3).text, "26"); // +6 hidden columns + assertFalse(marks.get(4).major); + assertEquals(marks.get(4).column, 24); + assertNull(marks.get(4).text); } } diff --git a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java index 7fd7abc..f6dfed6 100644 --- a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java +++ b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java @@ -2,6 +2,7 @@ package jalview.renderer.seqfeatures; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; @@ -285,6 +286,28 @@ public class FeatureColourFinderTest } @Test(groups = "Functional") + public void testFindFeatureAtEnd() + { + /* + * terminal residue feature + */ + seq.addSequenceFeature(new SequenceFeature("PDBRESNUM", "pdb res 1", + seq.getEnd(), seq.getEnd(), Float.NaN, "1seq.pdb")); + fr.setColour("PDBRESNUM", new FeatureColour(Color.red)); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * final column should have PDBRESNUM feature, the others not + */ + Color c = finder.findFeatureColour(Color.blue, seq, + seq.getLength() - 2); + assertNotEquals(c, Color.red); + c = finder.findFeatureColour(Color.blue, seq, seq.getLength() - 1); + assertEquals(c, Color.red); + } + + @Test(groups = "Functional") public void testFindFeatureColour_graduatedFeatureColour() { seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2, diff --git a/utils/checkstyle/checkstyle-suppress.xml b/utils/checkstyle/checkstyle-suppress.xml index ac9e260..122b8d0 100644 --- a/utils/checkstyle/checkstyle-suppress.xml +++ b/utils/checkstyle/checkstyle-suppress.xml @@ -21,10 +21,12 @@ - - - - + + + + + +