From: gmungoc Date: Tue, 8 Oct 2019 09:49:13 +0000 (+0100) Subject: Merge branch 'develop' into Jalview-JS/develop X-Git-Tag: Release_2_11_4_0~45^2~18^2~122 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=0b573ed90b14079f7326281f50c0c9cffdace586;p=jalview.git Merge branch 'develop' into Jalview-JS/develop Conflicts: src/jalview/analysis/AlignmentUtils.java src/jalview/analysis/CrossRef.java src/jalview/gui/FeatureSettings.java src/jalview/gui/PopupMenu.java --- 0b573ed90b14079f7326281f50c0c9cffdace586 diff --cc .classpath index 0a7e4b3,0000000..a85d514 mode 100644,000000..100644 --- a/.classpath +++ b/.classpath @@@ -1,206 -1,0 +1,23 @@@ + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --cc src/jalview/analysis/AlignmentUtils.java index 9b8d220,55efaa5..4cd1fc4 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@@ -20,8 -20,9 +20,7 @@@ */ package jalview.analysis; --import static jalview.io.gff.GffConstants.CLINICAL_SIGNIFICANCE; -- + import jalview.commands.RemoveGapColCommand; import jalview.datamodel.AlignedCodon; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping; @@@ -37,7 -38,7 +36,6 @@@ import jalview.datamodel.SequenceFeatur import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.datamodel.features.SequenceFeatures; --import jalview.io.gff.Gff3Helper; import jalview.io.gff.SequenceOntologyI; import jalview.schemes.ResidueProperties; import jalview.util.Comparison; @@@ -45,10 -46,10 +43,7 @@@ import jalview.util.DBRefUtils import jalview.util.IntRangeComparator; import jalview.util.MapList; import jalview.util.MappingUtils; --import jalview.util.StringUtils; --import java.io.UnsupportedEncodingException; --import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@@ -1602,12 -1603,11 +1597,12 @@@ public class AlignmentUtil return false; } String name = seq2.getName(); - final DBRefEntry[] xrefs = seq1.getDBRefs(); + final List xrefs = seq1.getDBRefs(); if (xrefs != null) { - for (DBRefEntry xref : xrefs) + for (int ix = 0, nx = xrefs.size(); ix < nx; ix++) { - DBRefEntry xref = xrefs.get(ix); ++ DBRefEntry xref = xrefs.get(ix); String xrefName = xref.getSource() + "|" + xref.getAccessionId(); // case-insensitive test, consistent with DBRefEntry.equalRef() if (xrefName.equalsIgnoreCase(name)) @@@ -2075,15 -2099,13 +2094,15 @@@ List direct = new ArrayList<>(); HashSet directSources = new HashSet<>(); - if (contig.getDBRefs() != null) + List refs = contig.getDBRefs(); + if (refs != null) { - for (DBRefEntry dbr : contig.getDBRefs()) + for (int ib = 0, nb = refs.size(); ib < nb; ib++) { - DBRefEntry dbr = refs.get(ib); - MapList map; - if (dbr.hasMap() && dbr.getMap().getMap().isTripletMap()) ++ DBRefEntry dbr = refs.get(ib); ++ MapList map; + if (dbr.hasMap() && (map = dbr.getMap().getMap()).isTripletMap()) { - MapList map = dbr.getMap().getMap(); // check if map is the CDS mapping if (mapping.getMap().equals(map)) { @@@ -2099,16 -2121,15 +2118,16 @@@ List propagated = new ArrayList<>(); // and generate appropriate mappings - for (DBRefEntry cdsref : direct) + for (int ic = 0, nc = direct.size(); ic < nc; ic++) { - DBRefEntry cdsref = direct.get(ic); - Mapping m = cdsref.getMap(); ++ DBRefEntry cdsref = direct.get(ic); ++ Mapping m = cdsref.getMap(); // clone maplist and mapping MapList cdsposmap = new MapList( Arrays.asList(new int[][] { new int[] { cdsSeq.getStart(), cdsSeq.getEnd() } }), - cdsref.getMap().getMap().getToRanges(), 3, 1); - Mapping cdsmap = new Mapping(cdsref.getMap().getTo(), - cdsref.getMap().getMap()); + m.getMap().getToRanges(), 3, 1); - Mapping cdsmap = new Mapping(m.getTo(),m.getMap()); ++ Mapping cdsmap = new Mapping(m.getTo(), m.getMap()); // create dbref DBRefEntry newref = new DBRefEntry(cdsref.getSource(), @@@ -2327,14 -2352,10 +2350,14 @@@ int phase = 0; try { - phase = Integer.parseInt(sf.getPhase()); + String s = sf.getPhase(); + if (s != null) + { + phase = Integer.parseInt(s); + } } catch (NumberFormatException e) { - // SwingJS -- need to avoid these. - // ignore ++ // leave as zero } /* * phase > 0 on first codon means 5' incomplete - skip to the start @@@ -2368,393 -2389,97 +2391,6 @@@ } /** - * Maps exon features from dna to protein, and computes variants in peptide - * product generated by variants in dna, and adds them as sequence_variant - * features on the protein sequence. Returns the number of variant features - * added. - * - * @param dnaSeq - * @param peptide - * @param dnaToProtein - */ - public static int computeProteinFeatures(SequenceI dnaSeq, - SequenceI peptide, MapList dnaToProtein) - { - while (dnaSeq.getDatasetSequence() != null) - { - dnaSeq = dnaSeq.getDatasetSequence(); - } - while (peptide.getDatasetSequence() != null) - { - peptide = peptide.getDatasetSequence(); - } - - transferFeatures(dnaSeq, peptide, dnaToProtein, SequenceOntologyI.EXON); - - /* - * compute protein variants from dna variants and codon mappings; - * NB - alternatively we could retrieve this using the REST service e.g. - * http://rest.ensembl.org/overlap/translation - * /ENSP00000288602?feature=transcript_variation;content-type=text/xml - * which would be a bit slower but possibly more reliable - */ - - /* - * build a map with codon variations for each potentially varying peptide - */ - LinkedHashMap[]> variants = buildDnaVariantsMap( - dnaSeq, dnaToProtein); - - /* - * scan codon variations, compute peptide variants and add to peptide sequence - */ - int count = 0; - for (Entry[]> variant : variants.entrySet()) - { - int peptidePos = variant.getKey(); - List[] codonVariants = variant.getValue(); - count += computePeptideVariants(peptide, peptidePos, codonVariants); - } - - return count; - } - - /** - * Computes non-synonymous peptide variants from codon variants and adds them - * as sequence_variant features on the protein sequence (one feature per - * allele variant). Selected attributes (variant id, clinical significance) - * are copied over to the new features. - * - * @param peptide - * the protein sequence - * @param peptidePos - * the position to compute peptide variants for - * @param codonVariants - * a list of dna variants per codon position - * @return the number of features added - */ - static int computePeptideVariants(SequenceI peptide, int peptidePos, - List[] codonVariants) - { - String residue = String.valueOf(peptide.getCharAt(peptidePos - 1)); - int count = 0; - String base1 = codonVariants[0].get(0).base; - String base2 = codonVariants[1].get(0).base; - String base3 = codonVariants[2].get(0).base; - - /* - * variants in first codon base - */ - for (DnaVariant dnavar : codonVariants[0]) - { - if (dnavar.variant != null) - { - String alleles = (String) dnavar.variant.getValue(Gff3Helper.ALLELES); - if (alleles != null) - { - for (String base : alleles.split(",")) - { - if (!base1.equalsIgnoreCase(base)) - { - String codon = base.toUpperCase() + base2.toLowerCase() - + base3.toLowerCase(); - String canonical = base1.toUpperCase() + base2.toLowerCase() - + base3.toLowerCase(); - if (addPeptideVariant(peptide, peptidePos, residue, dnavar, - codon, canonical)) - { - count++; - } - } - } - } - } - } - - /* - * variants in second codon base - */ - for (DnaVariant var : codonVariants[1]) - { - if (var.variant != null) - { - String alleles = (String) var.variant.getValue(Gff3Helper.ALLELES); - if (alleles != null) - { - for (String base : alleles.split(",")) - { - if (!base2.equalsIgnoreCase(base)) - { - String codon = base1.toLowerCase() + base.toUpperCase() - + base3.toLowerCase(); - String canonical = base1.toLowerCase() + base2.toUpperCase() - + base3.toLowerCase(); - if (addPeptideVariant(peptide, peptidePos, residue, var, - codon, canonical)) - { - count++; - } - } - } - } - } - } - - /* - * variants in third codon base - */ - for (DnaVariant var : codonVariants[2]) - { - if (var.variant != null) - { - String alleles = (String) var.variant.getValue(Gff3Helper.ALLELES); - if (alleles != null) - { - for (String base : alleles.split(",")) - { - if (!base3.equalsIgnoreCase(base)) - { - String codon = base1.toLowerCase() + base2.toLowerCase() - + base.toUpperCase(); - String canonical = base1.toLowerCase() + base2.toLowerCase() - + base3.toUpperCase(); - if (addPeptideVariant(peptide, peptidePos, residue, var, - codon, canonical)) - { - count++; - } - } - } - } - } - } - - return count; - } - - /** -- * Helper method that adds a peptide variant feature. ID and -- * clinical_significance attributes of the dna variant (if present) are copied -- * to the new feature. -- * -- * @param peptide -- * @param peptidePos -- * @param residue -- * @param var -- * @param codon -- * the variant codon e.g. aCg -- * @param canonical -- * the 'normal' codon e.g. aTg -- * @return true if a feature was added, else false -- */ -- static boolean addPeptideVariant(SequenceI peptide, int peptidePos, -- String residue, DnaVariant var, String codon, String canonical) -- { -- /* -- * get peptide translation of codon e.g. GAT -> D -- * note that variants which are not single alleles, -- * e.g. multibase variants or HGMD_MUTATION etc -- * are currently ignored here -- */ -- String trans = codon.contains("-") ? null -- : (codon.length() > CODON_LENGTH ? null -- : ResidueProperties.codonTranslate(codon)); -- if (trans == null) -- { -- return false; -- } -- String desc = canonical + "/" + codon; -- String featureType = ""; -- if (trans.equals(residue)) -- { -- featureType = SequenceOntologyI.SYNONYMOUS_VARIANT; -- } -- else if (ResidueProperties.STOP.equals(trans)) -- { -- featureType = SequenceOntologyI.STOP_GAINED; -- } -- else -- { -- String residue3Char = StringUtils -- .toSentenceCase(ResidueProperties.aa2Triplet.get(residue)); -- String trans3Char = StringUtils -- .toSentenceCase(ResidueProperties.aa2Triplet.get(trans)); -- desc = "p." + residue3Char + peptidePos + trans3Char; -- featureType = SequenceOntologyI.NONSYNONYMOUS_VARIANT; -- } -- SequenceFeature sf = new SequenceFeature(featureType, desc, peptidePos, -- peptidePos, var.getSource()); -- -- StringBuilder attributes = new StringBuilder(32); -- String id = (String) var.variant.getValue(VARIANT_ID); -- if (id != null) -- { -- if (id.startsWith(SEQUENCE_VARIANT)) -- { -- id = id.substring(SEQUENCE_VARIANT.length()); -- } -- sf.setValue(VARIANT_ID, id); -- attributes.append(VARIANT_ID).append("=").append(id); -- // TODO handle other species variants JAL-2064 -- StringBuilder link = new StringBuilder(32); -- try -- { -- link.append(desc).append(" ").append(id).append( -- "|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=") -- .append(URLEncoder.encode(id, "UTF-8")); -- sf.addLink(link.toString()); -- } catch (UnsupportedEncodingException e) -- { -- // as if -- } -- } -- String clinSig = (String) var.variant.getValue(CLINICAL_SIGNIFICANCE); -- if (clinSig != null) -- { -- sf.setValue(CLINICAL_SIGNIFICANCE, clinSig); -- attributes.append(";").append(CLINICAL_SIGNIFICANCE).append("=") -- .append(clinSig); -- } -- peptide.addSequenceFeature(sf); -- if (attributes.length() > 0) -- { -- sf.setAttributes(attributes.toString()); -- } -- return true; -- } -- -- /** - * Builds a map whose key is position in the protein sequence, and value is a - * list of the base and all variants for each corresponding codon position. - *

- * This depends on dna variants being held as a comma-separated list as - * property "alleles" on variant features. - * - * @param dnaSeq - * @param dnaToProtein - * @return - */ - @SuppressWarnings("unchecked") - static LinkedHashMap[]> buildDnaVariantsMap( - SequenceI dnaSeq, MapList dnaToProtein) - { - /* - * map from peptide position to all variants of the codon which codes for it - * LinkedHashMap ensures we keep the peptide features in sequence order - */ - LinkedHashMap[]> variants = new LinkedHashMap<>(); - - List dnaFeatures = dnaSeq.getFeatures() - .getFeaturesByOntology(SequenceOntologyI.SEQUENCE_VARIANT); - if (dnaFeatures.isEmpty()) - { - return variants; - } - - int dnaStart = dnaSeq.getStart(); - int[] lastCodon = null; - int lastPeptidePostion = 0; - - /* - * build a map of codon variations for peptides - */ - for (SequenceFeature sf : dnaFeatures) - { - int dnaCol = sf.getBegin(); - if (dnaCol != sf.getEnd()) - { - // not handling multi-locus variant features - continue; - } - - /* - * ignore variant if not a SNP - */ - String alls = (String) sf.getValue(Gff3Helper.ALLELES); - if (alls == null) - { - continue; // non-SNP VCF variant perhaps - can't process this - } - - String[] alleles = alls.toUpperCase().split(","); - boolean isSnp = true; - for (String allele : alleles) - { - if (allele.trim().length() > 1) - { - isSnp = false; - } - } - if (!isSnp) - { - continue; - } - - int[] mapsTo = dnaToProtein.locateInTo(dnaCol, dnaCol); - if (mapsTo == null) - { - // feature doesn't lie within coding region - continue; - } - int peptidePosition = mapsTo[0]; - List[] codonVariants = variants.get(peptidePosition); - if (codonVariants == null) - { - codonVariants = new ArrayList[CODON_LENGTH]; - codonVariants[0] = new ArrayList<>(); - codonVariants[1] = new ArrayList<>(); - codonVariants[2] = new ArrayList<>(); - variants.put(peptidePosition, codonVariants); - } - - /* - * get this peptide's codon positions e.g. [3, 4, 5] or [4, 7, 10] - */ - int[] codon = peptidePosition == lastPeptidePostion ? lastCodon - : MappingUtils.flattenRanges(dnaToProtein.locateInFrom( - peptidePosition, peptidePosition)); - lastPeptidePostion = peptidePosition; - lastCodon = codon; - - /* - * save nucleotide (and any variant) for each codon position - */ - for (int codonPos = 0; codonPos < CODON_LENGTH; codonPos++) - { - String nucleotide = String.valueOf( - dnaSeq.getCharAt(codon[codonPos] - dnaStart)).toUpperCase(); - List codonVariant = codonVariants[codonPos]; - if (codon[codonPos] == dnaCol) - { - if (!codonVariant.isEmpty() - && codonVariant.get(0).variant == null) - { - /* - * already recorded base value, add this variant - */ - codonVariant.get(0).variant = sf; - } - else - { - /* - * add variant with base value - */ - codonVariant.add(new DnaVariant(nucleotide, sf)); - } - } - else if (codonVariant.isEmpty()) - { - /* - * record (possibly non-varying) base value - */ - codonVariant.add(new DnaVariant(nucleotide)); - } - } - } - return variants; - } - - /** * Makes an alignment with a copy of the given sequences, adding in any * non-redundant sequences which are mapped to by the cross-referenced * sequences. @@@ -2774,21 -2499,15 +2410,21 @@@ SequenceIdMatcher matcher = new SequenceIdMatcher(seqs); if (xrefs != null) { - // BH 2019.01.25 streamlined this triply nested loop to remove all iterators - for (SequenceI xref : xrefs) ++ // BH 2019.01.25 recoded to remove iterators + + for (int ix = 0, nx = xrefs.length; ix < nx; ix++) { - DBRefEntry[] dbrefs = xref.getDBRefs(); + SequenceI xref = xrefs[ix]; + List dbrefs = xref.getDBRefs(); if (dbrefs != null) { - for (DBRefEntry dbref : dbrefs) + for (int ir = 0, nir = dbrefs.size(); ir < nir; ir++) { - DBRefEntry dbref = dbrefs.get(ir); - Mapping map = dbref.getMap(); - SequenceI mto; - if (dbref.getMap() == null || dbref.getMap().getTo() == null - || dbref.getMap().getTo().isProtein() != isProtein) ++ DBRefEntry dbref = dbrefs.get(ir); ++ Mapping map = dbref.getMap(); ++ SequenceI mto; + if (map == null || (mto = map.getTo()) == null + || mto.isProtein() != isProtein) { continue; } diff --cc src/jalview/analysis/CrossRef.java index 00bb63a,40401fb..c54357e --- a/src/jalview/analysis/CrossRef.java +++ b/src/jalview/analysis/CrossRef.java @@@ -483,11 -483,9 +483,11 @@@ public class CrossRe private void removeAlreadyRetrievedSeqs(List sourceRefs, boolean fromDna) { - List dbrSourceSet = new ArrayList(sourceRefs); - DBRefEntry[] dbrSourceSet = sourceRefs.toArray(new DBRefEntry[0]); - for (SequenceI sq : dataset.getSequences()) ++ List dbrSourceSet = new ArrayList<>(sourceRefs); + List dsSeqs = dataset.getSequences(); + for (int ids = 0, nds = dsSeqs.size(); ids < nds; ids++) { + SequenceI sq = dsSeqs.get(ids); boolean dupeFound = false; // !fromDna means we are looking only for nucleotide sequences, not // protein diff --cc src/jalview/gui/FeatureSettings.java index 9fe2e92,11d5e39..5aa9499 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@@ -143,9 -145,11 +147,11 @@@ public class FeatureSettings extends JP */ Object[][] originalData; - private float originalTransparency; + float originalTransparency; - Map originalFilters; + private ViewStyleI originalViewStyle; + + private Map originalFilters; final JInternalFrame frame; @@@ -282,9 -288,11 +290,10 @@@ if (evt.isPopupTrigger()) { Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN); - popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(), - evt.getY()); + showPopupMenu(selectedRow, type, colour, evt.getPoint()); } - else if (evt.getClickCount() == 2) + else if (evt.getClickCount() == 2 + && table.columnAtPoint(pt) == TYPE_COLUMN) { boolean invertSelection = evt.isAltDown(); boolean toggleSelection = Platform.isControlDown(evt); @@@ -379,7 -398,7 +389,7 @@@ javax.swing.event.InternalFrameEvent evt) { fr.removePropertyChangeListener(change); -- }; ++ } }); frame.setLayer(JLayeredPane.PALETTE_LAYER); inConstruction = false; diff --cc src/jalview/gui/FeatureTypeSettings.java index bac9d9b,5b77dfc..200911d --- a/src/jalview/gui/FeatureTypeSettings.java +++ b/src/jalview/gui/FeatureTypeSettings.java @@@ -892,9 -876,9 +893,9 @@@ public class FeatureTypeSettings extend * save the colour, and repaint stuff */ fr.setColour(featureType, acg); - ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview); + refreshDisplay(updateStructsAndOverview); - updateColoursTab(); + updateColoursPanel(); } /** @@@ -1780,8 -1754,26 +1781,26 @@@ * (note this might now be an empty filter with no conditions) */ fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined); - ap.paintAlignment(true, true); + refreshDisplay(true); - updateFiltersTab(); + updateFiltersPanel(); } + + /** + * Repaints alignment, structure and overview (if shown). If there is a + * complementary view which is showing this view's features, then also + * repaints that. + * + * @param updateStructsAndOverview + */ + void refreshDisplay(boolean updateStructsAndOverview) + { + ap.paintAlignment(true, updateStructsAndOverview); + AlignViewportI complement = ap.getAlignViewport().getCodingComplement(); + if (complement != null && complement.isShowComplementFeatures()) + { + AlignFrame af2 = Desktop.getAlignFrameFor(complement); + af2.alignPanel.paintAlignment(true, updateStructsAndOverview); + } + } } diff --cc src/jalview/gui/PopupMenu.java index ad33110,415054e..8a37952 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@@ -51,11 -52,10 +53,12 @@@ import jalview.util.Comparison import jalview.util.GroupUrlLink; import jalview.util.GroupUrlLink.UrlStringTooLongException; import jalview.util.MessageManager; +import jalview.util.Platform; import jalview.util.StringUtils; import jalview.util.UrlLink; + import jalview.viewmodel.seqfeatures.FeatureRendererModel; +import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@@ -786,42 -863,19 +869,44 @@@ public class PopupMenu extends JPopupMe /** * Opens a panel showing a text report of feature dteails * + * @param seqName + * * @param sf */ - protected void showFeatureDetails(SequenceFeature sf) + protected void showFeatureDetails(String seqName, SequenceFeature sf) { - CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer(); - // it appears Java's CSS does not support border-collapse :-( - cap.addStylesheetRule("table { border-collapse: collapse;}"); - cap.addStylesheetRule("table, td, th {border: 1px solid black;}"); - cap.setText(sf.getDetailsReport(seqName)); - - Desktop.addInternalFrame(cap, + JInternalFrame details; + if (Platform.isJS()) + { + details = new JInternalFrame(); + JPanel panel = new JPanel(new BorderLayout()); + panel.setOpaque(true); + panel.setBackground(Color.white); + // TODO JAL-3026 set style of table correctly for feature details + JLabel reprt = new JLabel(MessageManager + .formatMessage("label.html_content", new Object[] - { sf.getDetailsReport() })); ++ { sf.getDetailsReport(seqName) })); + reprt.setBackground(Color.WHITE); + reprt.setOpaque(true); + panel.add(reprt, BorderLayout.CENTER); + details.setContentPane(panel); + details.pack(); + } + else + /** + * Java only + * + * @j2sIgnore + */ + { + CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer(); + // it appears Java's CSS does not support border-collaps :-( + cap.addStylesheetRule("table { border-collapse: collapse;}"); + cap.addStylesheetRule("table, td, th {border: 1px solid black;}"); - cap.setText(sf.getDetailsReport()); ++ cap.setText(sf.getDetailsReport(seqName)); + details = cap; + } + Desktop.addInternalFrame(details, MessageManager.getString("label.feature_details"), 500, 500); } @@@ -1058,7 -1110,7 +1143,6 @@@ Cache.log.error("Exception for GroupURLLink '" + link + "'", foo); continue; } -- ; if (!urlLink.isValid()) { Cache.log.error(urlLink.getInvalidMessage()); @@@ -1697,15 -1747,10 +1779,10 @@@ createSequenceDetailsReport(ap.av.getSequenceSelection()); } - protected void sequenceDetails_actionPerformed() - { - createSequenceDetailsReport(new SequenceI[] { sequence }); - } - public void createSequenceDetailsReport(SequenceI[] sequences) { - CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer(); StringBuilder contents = new StringBuilder(128); + contents.append(""); for (SequenceI seq : sequences) { contents.append("

" + MessageManager.formatMessage( @@@ -1974,19 -2008,17 +2053,19 @@@ */ protected void outline_actionPerformed() { - SequenceGroup sg = getGroup(); - Color col = JColorChooser.showDialog(this, - MessageManager.getString("label.select_outline_colour"), - Color.BLUE); - - if (col != null) + String title = MessageManager + .getString("label.select_outline_colour"); + ColourChooserListener listener = new ColourChooserListener() { - sg.setOutlineColour(col); - } - - refresh(); + @Override + public void colourSelected(Color c) + { + getGroup().setOutlineColour(c); + refresh(); - }; ++ } + }; + JalviewColourChooser.showColourChooser(Desktop.getDesktop(), + title, Color.BLUE, listener); } /** @@@ -2146,31 -2184,7 +2225,12 @@@ } } - public void colourByStructure(String pdbid) - { - Annotation[] anots = ap.av.getStructureSelectionManager() - .colourSequenceFromStructure(sequence, pdbid); - - AlignmentAnnotation an = new AlignmentAnnotation("Structure", - "Coloured by " + pdbid, anots); - - ap.av.getAlignment().addAnnotation(an); - an.createSequenceMapping(sequence, 0, true); - // an.adjustForAlignment(); - ap.av.getAlignment().setAnnotationIndex(an, 0); - - ap.adjustAnnotationHeight(); - - sequence.addAlignmentAnnotation(an); - - } - - public void editSequence_actionPerformed(ActionEvent actionEvent) + /** + * Shows a dialog where sequence characters may be edited. Any changes are + * applied, and added as an available 'Undo' item in the edit commands + * history. + */ + public void editSequence_actionPerformed() { SequenceGroup sg = ap.av.getSelectionGroup(); @@@ -2182,30 -2197,26 +2243,29 @@@ } EditNameDialog dialog = new EditNameDialog( - sequence.getSequenceAsString(sg.getStartRes(), - sg.getEndRes() + 1), - seq.getSequenceAsString(sg.getStartRes(), - sg.getEndRes() + 1), - null, MessageManager.getString("label.edit_sequence"), null, ++ seq.getSequenceAsString(sg.getStartRes(), sg.getEndRes() + 1), + null, MessageManager.getString("label.edit_sequence"), null); + dialog.showDialog(ap.alignFrame, MessageManager.getString("label.edit_sequence"), - ap.alignFrame); - - if (dialog.accept) - { - EditCommand editCommand = new EditCommand( - MessageManager.getString("label.edit_sequences"), - Action.REPLACE, - dialog.getName().replace(' ', ap.av.getGapCharacter()), - sg.getSequencesAsArray(ap.av.getHiddenRepSequences()), - sg.getStartRes(), sg.getEndRes() + 1, ap.av.getAlignment()); - - ap.alignFrame.addHistoryItem(editCommand); - - ap.av.firePropertyChange("alignment", null, - ap.av.getAlignment().getSequences()); - } + new Runnable() + { + @Override + public void run() + { + EditCommand editCommand = new EditCommand( + MessageManager.getString("label.edit_sequences"), + Action.REPLACE, + dialog.getName().replace(' ', + ap.av.getGapCharacter()), + sg.getSequencesAsArray( + ap.av.getHiddenRepSequences()), + sg.getStartRes(), sg.getEndRes() + 1, + ap.av.getAlignment()); + ap.alignFrame.addHistoryItem(editCommand); + ap.av.firePropertyChange("alignment", null, + ap.av.getAlignment().getSequences()); + } + }); } } diff --cc src/jalview/gui/SeqCanvas.java index e962709,2832796..b27208a --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@@ -53,11 -53,10 +53,11 @@@ import javax.swing.JPanel * Wrapped mode, but not the scale above in Unwrapped mode. * */ -public class SeqCanvas extends JComponent implements ViewportListenerI +@SuppressWarnings("serial") +public class SeqCanvas extends JPanel implements ViewportListenerI { - /* - * pixels gap between sequences and annotations when in wrapped mode + /** + * vertical gap in pixels between sequences and annotations when in wrapped mode */ static final int SEQS_ANNOTATION_GAP = 3; diff --cc src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index 54bb4ee,aba5601..459a28b --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@@ -1157,34 -1161,100 +1162,127 @@@ public abstract class FeatureRendererMo return filter == null ? true : filter.matches(sf); } + /** + * Answers true unless the specified group is set to hidden. Defaults to true + * if group visibility is not set. + * + * @param group + * @return + */ + public boolean isGroupVisible(String group) + { + if (!featureGroups.containsKey(group)) + { + return true; + } + return featureGroups.get(group); + } + + /** + * Orders features in render precedence (last in order is last to render, so + * displayed on top of other features) + * + * @param order + */ + public void orderFeatures(Comparator order) + { + Arrays.sort(renderOrder, order); + } + @Override + public MappedFeatures findComplementFeaturesAtResidue(SequenceI sequence, + int pos) + { + SequenceI ds = sequence.getDatasetSequence(); + if (ds == null) + { + ds = sequence; + } + final char residue = ds.getCharAt(pos - ds.getStart()); + + List found = new ArrayList<>(); + List mappings = this.av.getAlignment() + .getCodonFrame(sequence); + + /* + * fudge: if no mapping found, check the complementary alignment + * todo: only store in one place? StructureSelectionManager? + */ + if (mappings.isEmpty()) + { + mappings = this.av.getCodingComplement().getAlignment() + .getCodonFrame(sequence); + } + + /* + * todo: direct lookup of CDS for peptide and vice-versa; for now, + * have to search through an unordered list of mappings for a candidate + */ + Mapping mapping = null; + SequenceI mapFrom = null; + + for (AlignedCodonFrame acf : mappings) + { + mapping = acf.getMappingForSequence(sequence); + if (mapping == null || !mapping.getMap().isTripletMap()) + { + continue; // we are only looking for 3:1 or 1:3 mappings + } + SearchResultsI sr = new SearchResults(); + acf.markMappedRegion(ds, pos, sr); + for (SearchResultMatchI match : sr.getResults()) + { + int fromRes = match.getStart(); + int toRes = match.getEnd(); + mapFrom = match.getSequence(); + List fs = findFeaturesAtResidue( + match.getSequence(), fromRes, toRes); + for (SequenceFeature sf : fs) + { + if (!found.contains(sf)) + { + found.add(sf); + } + } + } + + /* + * just take the first mapped features we find + */ + if (!found.isEmpty()) + { + break; + } + } + if (found.isEmpty()) + { + return null; + } + + /* + * sort by renderorder, inefficiently + */ + List result = new ArrayList<>(); + for (String type : renderOrder) + { + for (SequenceFeature sf : found) + { + if (type.equals(sf.getType())) + { + result.add(sf); + if (result.size() == found.size()) + { + return new MappedFeatures(mapping, mapFrom, pos, residue, + result); + } + } + } + } + + return new MappedFeatures(mapping, mapFrom, pos, residue, result); + } + + @Override public boolean isVisible(SequenceFeature feature) { if (feature == null)