From: gmungoc Date: Mon, 17 Jun 2019 15:59:08 +0000 (+0100) Subject: JAL-2792 JAL-3187 linked features (if shown) in Feature details submenu X-Git-Tag: Release_2_11_1_0~41 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;ds=sidebyside;h=83dd6154fdf5f727bc587ef58790c08e8404b757;p=jalview.git JAL-2792 JAL-3187 linked features (if shown) in Feature details submenu --- diff --git a/src/jalview/datamodel/MappedFeatures.java b/src/jalview/datamodel/MappedFeatures.java index 4c63916..2f90a7c 100644 --- a/src/jalview/datamodel/MappedFeatures.java +++ b/src/jalview/datamodel/MappedFeatures.java @@ -17,27 +17,27 @@ import java.util.List; public class MappedFeatures { /* - * the mapping from CDS to peptide + * the mapping from one sequence to another */ public final Mapping mapping; /** - * the CDS sequence mapped to + * the sequence mapped to */ public final SequenceI fromSeq; /* - * the residue position in the peptide sequence + * the residue position in the sequence mapped from */ public final int fromPosition; /* - * the peptide residue at the position + * the residue at fromPosition */ public final char fromResidue; /* - * features on CDS that overlap the codon positions + * features on the sequence mapped to that overlap the mapped positions */ public final List features; diff --git a/src/jalview/datamodel/SequenceFeature.java b/src/jalview/datamodel/SequenceFeature.java index e7e1342..c8a7def 100755 --- a/src/jalview/datamodel/SequenceFeature.java +++ b/src/jalview/datamodel/SequenceFeature.java @@ -607,11 +607,11 @@ public class SequenceFeature implements FeatureLocationI /** * Answers an html-formatted report of feature details * - * @param sequence + * @param seqName * * @return */ - public String getDetailsReport(SequenceI sequence) + public String getDetailsReport(String seqName) { FeatureSourceI metadata = FeatureSources.getInstance() .getSource(source); @@ -619,7 +619,7 @@ public class SequenceFeature implements FeatureLocationI StringBuilder sb = new StringBuilder(128); sb.append("
"); sb.append(""); - sb.append(String.format(ROW_DATA, "Location", sequence.getName(), + sb.append(String.format(ROW_DATA, "Location", seqName, begin == end ? begin : begin + (isContactFeature() ? ":" : "-") + end)); sb.append(String.format(ROW_DATA, "Type", type, "")); diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index d11d7a1..2b1507a 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -22,7 +22,6 @@ package jalview.gui; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Sequence; -import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.gui.SeqPanel.MousePos; @@ -369,28 +368,12 @@ public class IdPanel extends JPanel } Sequence sq = (Sequence) av.getAlignment().getSequenceAt(pos.seqIndex); - - /* - * build a new links menu based on the current links - * and any non-positional features - */ - List features = null; if (sq != null) { - List nlinks = Preferences.sequenceUrlLinks.getLinksForMenu(); - features = sq.getFeatures().getNonPositionalFeatures(); - for (SequenceFeature sf : features) - { - if (sf.links != null) - { - nlinks.addAll(sf.links); - } - } + PopupMenu pop = new PopupMenu(alignPanel, sq, + Preferences.getGroupURLLinks()); + pop.show(this, e.getX(), e.getY()); } - - PopupMenu pop = new PopupMenu(alignPanel, sq, features, - Preferences.getGroupURLLinks()); - pop.show(this, e.getX(), e.getY()); } /** diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 06e35cd..415054e 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -24,6 +24,7 @@ import jalview.analysis.AAFrequency; import jalview.analysis.AlignmentAnnotationUtils; import jalview.analysis.AlignmentUtils; import jalview.analysis.Conservation; +import jalview.api.AlignViewportI; import jalview.bin.Cache; import jalview.commands.ChangeCaseCommand; import jalview.commands.EditCommand; @@ -32,6 +33,7 @@ import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.HiddenColumns; +import jalview.datamodel.MappedFeatures; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; @@ -46,11 +48,13 @@ import jalview.schemes.ColourSchemeI; import jalview.schemes.ColourSchemes; import jalview.schemes.PIDColourScheme; import jalview.schemes.ResidueColourScheme; +import jalview.util.Comparison; import jalview.util.GroupUrlLink; import jalview.util.GroupUrlLink.UrlStringTooLongException; import jalview.util.MessageManager; import jalview.util.StringUtils; import jalview.util.UrlLink; +import jalview.viewmodel.seqfeatures.FeatureRendererModel; import java.awt.Color; import java.awt.event.ActionEvent; @@ -64,6 +68,7 @@ import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.SortedMap; import java.util.TreeMap; import java.util.Vector; @@ -344,29 +349,33 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener * Constructor for a PopupMenu for a click in the alignment panel (on a residue) * * @param ap + * the panel in which the mouse is clicked * @param seq - * @param features - * sequence features overlapping the clicked residue + * the sequence under the mouse + * @throws NullPointerException + * if seq is null */ - public PopupMenu(final AlignmentPanel ap, SequenceI seq, - List features) + public PopupMenu(final AlignmentPanel ap, SequenceI seq, int column) { - this(false, ap, seq, features, null); + this(false, ap, seq, column, null); } /** * Constructor for a PopupMenu for a click in the sequence id panel * * @param alignPanel + * the panel in which the mouse is clicked * @param seq - * @param features - * non-positional features for the sequence + * the sequence under the mouse click * @param groupLinks + * templates for sequence external links + * @throws NullPointerException + * if seq is null */ public PopupMenu(final AlignmentPanel alignPanel, final SequenceI seq, - List features, List groupLinks) + List groupLinks) { - this(true, alignPanel, seq, features, groupLinks); + this(true, alignPanel, seq, -1, groupLinks); } /** @@ -376,14 +385,15 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener * @param fromIdPanel * @param alignPanel * @param seq - * @param features + * @param column + * aligned column position (0...) * @param groupLinks */ private PopupMenu(boolean fromIdPanel, final AlignmentPanel alignPanel, - final SequenceI seq, List features, - List groupLinks) + final SequenceI seq, final int column, List groupLinks) { + Objects.requireNonNull(seq); this.forIdPanel = fromIdPanel; this.ap = alignPanel; sequence = seq; @@ -409,7 +419,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener * 'reference annotations' that may be added to the alignment. First for the * currently selected sequence (if there is one): */ - final List selectedSequence = (forIdPanel + final List selectedSequence = (forIdPanel && seq != null ? Arrays.asList(seq) : Collections. emptyList()); buildAnnotationTypesMenus(seqShowAnnotationsMenu, @@ -701,11 +711,41 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener rnaStructureMenu.setVisible(false); } + addLinksAndFeatures(seq, column); + } + + /** + * Adds + *
    + *
  • configured sequence database links (ID panel popup menu)
  • + *
  • non-positional feature links (ID panel popup menu)
  • + *
  • positional feature links (alignment panel popup menu)
  • + *
  • feature details links (alignment panel popup menu)
  • + *
+ * If this panel is also showed complementary (CDS/protein) features, then links + * to their feature details are also added. + * + * @param seq + * @param column + */ + void addLinksAndFeatures(final SequenceI seq, final int column) + { + List features = null; + if (forIdPanel) + { + features = sequence.getFeatures().getNonPositionalFeatures(); + } + else + { + features = ap.getFeatureRenderer().findFeaturesAtColumn(sequence, + column + 1); + } + addLinks(seq, features); if (!forIdPanel) { - addFeatureDetails(features); + addFeatureDetails(features, seq, column); } } @@ -713,75 +753,127 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener * Add a link to show feature details for each sequence feature * * @param features + * @param column + * @param seq */ - protected void addFeatureDetails(List features) + protected void addFeatureDetails(List features, + SequenceI seq, int column) { - if (features == null || features.isEmpty()) + /* + * add features in CDS/protein complement at the corresponding + * position if configured to do so + */ + MappedFeatures mf = null; + if (ap.av.isShowComplementFeatures()) + { + if (!Comparison.isGap(sequence.getCharAt(column))) + { + AlignViewportI complement = ap.getAlignViewport() + .getCodingComplement(); + AlignFrame af = Desktop.getAlignFrameFor(complement); + FeatureRendererModel fr2 = af.getFeatureRenderer(); + int seqPos = sequence.findPosition(column); + mf = fr2.findComplementFeaturesAtResidue(sequence, seqPos); + } + } + + if (features.isEmpty() && mf == null) { + /* + * no features to show at this position + */ return; } + JMenu details = new JMenu( MessageManager.getString("label.feature_details")); add(details); + String name = seq.getName(); for (final SequenceFeature sf : features) { - int start = sf.getBegin(); - int end = sf.getEnd(); - String desc = null; - if (start == end) + addFeatureDetailsMenuItem(details, name, sf); + } + + if (mf != null) + { + name = mf.fromSeq == seq ? mf.mapping.getTo().getName() + : mf.fromSeq.getName(); + for (final SequenceFeature sf : mf.features) { - desc = String.format("%s %d", sf.getType(), start); + addFeatureDetailsMenuItem(details, name, sf); } - else + } + } + + /** + * A helper method to add one menu item whose action is to show details for one + * feature + * + * @param details + * @param seqName + * @param sf + */ + void addFeatureDetailsMenuItem(JMenu details, final String seqName, + final SequenceFeature sf) + { + int start = sf.getBegin(); + int end = sf.getEnd(); + String desc = null; + if (start == end) + { + desc = String.format("%s %d", sf.getType(), start); + } + else + { + desc = String.format("%s %d-%d", sf.getType(), start, end); + } + String tooltip = desc; + String description = sf.getDescription(); + if (description != null) + { + description = StringUtils.stripHtmlTags(description); + if (description.length() > 12) { - desc = String.format("%s %d-%d", sf.getType(), start, end); + desc = desc + " " + description.substring(0, 12) + ".."; } - String tooltip = desc; - String description = sf.getDescription(); - if (description != null) + else { - description = StringUtils.stripHtmlTags(description); - if (description.length() > 12) - { - desc = desc + " " + description.substring(0, 12) + ".."; - } - else - { - desc = desc + " " + description; - } - tooltip = tooltip + " " + description; + desc = desc + " " + description; } - if (sf.getFeatureGroup() != null) + tooltip = tooltip + " " + description; + } + if (sf.getFeatureGroup() != null) + { + tooltip = tooltip + (" (" + sf.getFeatureGroup() + ")"); + } + JMenuItem item = new JMenuItem(desc); + item.setToolTipText(tooltip); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) { - tooltip = tooltip + (" (" + sf.getFeatureGroup() + ")"); + showFeatureDetails(seqName, sf); } - JMenuItem item = new JMenuItem(desc); - item.setToolTipText(tooltip); - item.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - showFeatureDetails(sf); - } - }); - details.add(item); - } + }); + details.add(item); } /** * 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(sequence)); + cap.setText(sf.getDetailsReport(seqName)); Desktop.addInternalFrame(cap, MessageManager.getString("label.feature_details"), 500, 500); diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index e7bd200..14c3818 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -2254,11 +2254,11 @@ public class SeqPanel extends JPanel final int column = pos.column; final int seq = pos.seqIndex; SequenceI sequence = av.getAlignment().getSequenceAt(seq); - List features = ap.getFeatureRenderer() - .findFeaturesAtColumn(sequence, column + 1); - - PopupMenu pop = new PopupMenu(ap, sequence, features); - pop.show(this, evt.getX(), evt.getY()); + if (sequence != null) + { + PopupMenu pop = new PopupMenu(ap, sequence, column); + pop.show(this, evt.getX(), evt.getY()); + } } /** diff --git a/test/jalview/datamodel/SequenceFeatureTest.java b/test/jalview/datamodel/SequenceFeatureTest.java index 2774a16..cd8f9eb 100644 --- a/test/jalview/datamodel/SequenceFeatureTest.java +++ b/test/jalview/datamodel/SequenceFeatureTest.java @@ -278,13 +278,14 @@ public class SequenceFeatureTest public void testGetDetailsReport() { SequenceI seq = new Sequence("TestSeq", "PLRFQMD"); + String seqName = seq.getName(); // single locus, no group, no score SequenceFeature sf = new SequenceFeature("variant", "G,C", 22, 22, null); String expected = "
" + "" + "
LocationTestSeq22
Typevariant
DescriptionG,C
"; - assertEquals(expected, sf.getDetailsReport(seq)); + assertEquals(expected, sf.getDetailsReport(seqName)); // contact feature sf = new SequenceFeature("Disulphide Bond", "a description", 28, 31, @@ -292,7 +293,7 @@ public class SequenceFeatureTest expected = "
" + "" + "
LocationTestSeq28:31
TypeDisulphide Bond
Descriptiona description
"; - assertEquals(expected, sf.getDetailsReport(seq)); + assertEquals(expected, sf.getDetailsReport(seqName)); sf = new SequenceFeature("variant", "G,C", 22, 33, 12.5f, "group"); @@ -305,7 +306,7 @@ public class SequenceFeatureTest + "Groupgroup" + "ChildENSP002" + "ParentENSG001"; - assertEquals(expected, sf.getDetailsReport(seq)); + assertEquals(expected, sf.getDetailsReport(seqName)); /* * feature with embedded html link in description @@ -316,6 +317,6 @@ public class SequenceFeatureTest + "TypePfam" + "DescriptionFer2 Status: True Positive Pfam 8_8" + "GroupUniprot"; - assertEquals(expected, sf.getDetailsReport(seq)); + assertEquals(expected, sf.getDetailsReport(seqName)); } } diff --git a/test/jalview/gui/AlignFrameTest.java b/test/jalview/gui/AlignFrameTest.java index 454ff61..11a5f43 100644 --- a/test/jalview/gui/AlignFrameTest.java +++ b/test/jalview/gui/AlignFrameTest.java @@ -261,8 +261,11 @@ public class AlignFrameTest /* * apply 30% Conservation to group + * (notice menu action applies to selection group even if mouse click + * is at a sequence not in the group) */ - PopupMenu popupMenu = new PopupMenu(af.alignPanel, null, null); + PopupMenu popupMenu = new PopupMenu(af.alignPanel, al.getSequenceAt(2), + null); popupMenu.changeColour_actionPerformed(JalviewColourScheme.Strand .toString()); assertTrue(sg.getColourScheme() instanceof StrandColourScheme); @@ -540,7 +543,8 @@ public class AlignFrameTest sg.setStartRes(15); sg.setEndRes(25); av.setSelectionGroup(sg); - PopupMenu popupMenu = new PopupMenu(af.alignPanel, null, null); + PopupMenu popupMenu = new PopupMenu(af.alignPanel, al.getSequenceAt(0), + null); popupMenu.changeColour_actionPerformed(JalviewColourScheme.Strand .toString()); assertTrue(sg.getColourScheme() instanceof StrandColourScheme); diff --git a/test/jalview/gui/PopupMenuTest.java b/test/jalview/gui/PopupMenuTest.java index df86494..bf961d8 100644 --- a/test/jalview/gui/PopupMenuTest.java +++ b/test/jalview/gui/PopupMenuTest.java @@ -115,7 +115,7 @@ public class PopupMenuTest DataSourceType.PASTE, FileFormat.Fasta); AlignFrame af = new AlignFrame(alignment, 700, 500); parentPanel = new AlignmentPanel(af, af.getViewport()); - testee = new PopupMenu(parentPanel, null, null); + testee = new PopupMenu(parentPanel, alignment.getSequenceAt(0), null); int i = 0; for (SequenceI seq : alignment.getSequences()) { diff --git a/test/jalview/project/Jalview2xmlTests.java b/test/jalview/project/Jalview2xmlTests.java index 521ef81..5182ad4 100644 --- a/test/jalview/project/Jalview2xmlTests.java +++ b/test/jalview/project/Jalview2xmlTests.java @@ -842,13 +842,16 @@ public class Jalview2xmlTests extends Jalview2xmlBase /* * create a group with Strand colouring, 30% Conservation * and 40% PID threshold + * (notice menu action applies to selection group even if mouse click + * is at a sequence not in the group) */ SequenceGroup sg = new SequenceGroup(); sg.addSequence(al.getSequenceAt(0), false); sg.setStartRes(15); sg.setEndRes(25); av.setSelectionGroup(sg); - PopupMenu popupMenu = new PopupMenu(af.alignPanel, null, null); + PopupMenu popupMenu = new PopupMenu(af.alignPanel, al.getSequenceAt(2), + null); popupMenu.changeColour_actionPerformed( JalviewColourScheme.Strand.toString()); assertTrue(sg.getColourScheme() instanceof StrandColourScheme);