From: gmungoc Date: Tue, 30 May 2017 15:34:46 +0000 (+0100) Subject: Merge branch 'features/JAL-2446NCList' into features/JAL-2526sequenceCursor X-Git-Tag: Release_2_10_3b1~214^2~8 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=e9baffca44d19b36093953071f40478ea6db568e;hp=39ee6744c25d9beb29009ff82caa80418008a60e;p=jalview.git Merge branch 'features/JAL-2446NCList' into features/JAL-2526sequenceCursor --- diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index 83e8cf8..d5b7132 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -65,6 +65,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -819,9 +820,9 @@ public class APopupMenu extends java.awt.PopupMenu implements return; } - int rsize = 0, gSize = sg.getSize(); - SequenceI[] rseqs, seqs = new SequenceI[gSize]; - SequenceFeature[] tfeatures, features = new SequenceFeature[gSize]; + int gSize = sg.getSize(); + List seqs = new ArrayList(); + List features = new ArrayList(); for (int i = 0; i < gSize; i++) { @@ -829,25 +830,17 @@ public class APopupMenu extends java.awt.PopupMenu implements int end = sg.findEndRes(sg.getSequenceAt(i)); if (start <= end) { - seqs[rsize] = sg.getSequenceAt(i); - features[rsize] = new SequenceFeature(null, null, start, - end, "Jalview"); - rsize++; + seqs.add(sg.getSequenceAt(i)); + features.add(new SequenceFeature(null, null, start, end, + "Jalview")); } } - rseqs = new SequenceI[rsize]; - tfeatures = new SequenceFeature[rsize]; - System.arraycopy(seqs, 0, rseqs, 0, rsize); - System.arraycopy(features, 0, tfeatures, 0, rsize); - features = tfeatures; - seqs = rseqs; if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs, features, true, ap)) { ap.alignFrame.sequenceFeatures.setState(true); ap.av.setShowSequenceFeatures(true); - ; ap.highlightSearchResults(null); } } diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index b9e92e2..435e78d 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -53,6 +53,7 @@ import java.awt.event.MouseEvent; import java.awt.event.TextEvent; import java.awt.event.TextListener; import java.util.Hashtable; +import java.util.List; /** * DOCUMENT ME! @@ -182,8 +183,8 @@ public class FeatureRenderer extends * @param ap * @return */ - boolean amendFeatures(final SequenceI[] sequences, - final SequenceFeature[] features, boolean create, + boolean amendFeatures(final List sequences, + final List features, boolean create, final AlignmentPanel ap) { final Panel bigPanel = new Panel(new BorderLayout()); @@ -223,22 +224,20 @@ public class FeatureRenderer extends // ///////////////////////////////////// // /MULTIPLE FEATURES AT SELECTED RESIDUE - if (!create && features.length > 1) + if (!create && features.size() > 1) { panel = new Panel(new GridLayout(4, 1)); tmp = new Panel(); tmp.add(new Label("Select Feature: ")); overlaps = new Choice(); - for (int i = 0; i < features.length; i++) + for (SequenceFeature sf : features) { - String item = features[i].getType() + "/" + features[i].getBegin() - + "-" + features[i].getEnd(); - - if (features[i].getFeatureGroup() != null) + String item = sf.getType() + "/" + sf.getBegin() + "-" + + sf.getEnd(); + if (sf.getFeatureGroup() != null) { - item += " (" + features[i].getFeatureGroup() + ")"; + item += " (" + sf.getFeatureGroup() + ")"; } - overlaps.addItem(item); } @@ -253,15 +252,16 @@ public class FeatureRenderer extends if (index != -1) { featureIndex = index; - name.setText(features[index].getType()); - description.setText(features[index].getDescription()); - group.setText(features[index].getFeatureGroup()); - start.setText(features[index].getBegin() + ""); - end.setText(features[index].getEnd() + ""); + SequenceFeature sf = features.get(index); + name.setText(sf.getType()); + description.setText(sf.getDescription()); + group.setText(sf.getFeatureGroup()); + start.setText(sf.getBegin() + ""); + end.setText(sf.getEnd() + ""); SearchResultsI highlight = new SearchResults(); - highlight.addResult(sequences[0], features[index].getBegin(), - features[index].getEnd()); + highlight.addResult(sequences.get(0), sf.getBegin(), + sf.getEnd()); ap.seqPanel.seqCanvas.highlightSearchResults(highlight); @@ -269,8 +269,8 @@ public class FeatureRenderer extends FeatureColourI col = getFeatureStyle(name.getText()); if (col == null) { - Color generatedColour = ColorUtils - .createColourFromName(name.getText()); + Color generatedColour = ColorUtils.createColourFromName(name + .getText()); col = new FeatureColour(generatedColour); } @@ -328,16 +328,17 @@ public class FeatureRenderer extends * if feature type has not been supplied by the caller * (e.g. for Amend, or create features from Find) */ - boolean useLastDefaults = features[0].getType() == null; - String featureType = useLastDefaults ? lastFeatureAdded : features[0] + SequenceFeature firstFeature = features.get(0); + boolean useLastDefaults = firstFeature.getType() == null; + String featureType = useLastDefaults ? lastFeatureAdded : firstFeature .getType(); String featureGroup = useLastDefaults ? lastFeatureGroupAdded - : features[0].getFeatureGroup(); + : firstFeature.getFeatureGroup(); String title = create ? MessageManager .getString("label.create_new_sequence_features") : MessageManager.formatMessage("label.amend_delete_features", - new String[] { sequences[0].getName() }); + new String[] { sequences.get(0).getName() }); final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385, 240); @@ -362,9 +363,9 @@ public class FeatureRenderer extends }); } - start.setText(features[0].getBegin() + ""); - end.setText(features[0].getEnd() + ""); - description.setText(features[0].getDescription()); + start.setText(firstFeature.getBegin() + ""); + end.setText(firstFeature.getEnd() + ""); + description.setText(firstFeature.getDescription()); // lookup (or generate) the feature colour FeatureColourI fcol = getFeatureStyle(name.getText()); // simply display the feature color in a box @@ -406,7 +407,7 @@ public class FeatureRenderer extends if (!create) { - SequenceFeature sf = features[featureIndex]; + SequenceFeature sf = features.get(featureIndex); if (dialog.accept) { if (!colourPanel.isGcol) @@ -429,13 +430,13 @@ public class FeatureRenderer extends * replace the feature by deleting it and adding a new one * (to ensure integrity of SequenceFeatures data store) */ - sequences[0].deleteFeature(sf); + sequences.get(0).deleteFeature(sf); SequenceFeature newSf = new SequenceFeature(sf, newBegin, newEnd, enteredGroup, sf.getScore()); newSf.setDescription(enteredDesc); ffile.parseDescriptionHTML(newSf, false); // amend features dialog only updates one sequence at a time - sequences[0].addSequenceFeature(newSf); + sequences.get(0).addSequenceFeature(newSf); boolean typeOrGroupChanged = (!featureType.equals(sf.type) || !featureGroup .equals(sf.featureGroup)); @@ -447,7 +448,7 @@ public class FeatureRenderer extends } if (deleteFeature) { - sequences[0].deleteFeature(sf); + sequences.get(0).deleteFeature(sf); // ensure Feature Settings reflects removal of feature / group featuresAdded(); } @@ -459,13 +460,13 @@ public class FeatureRenderer extends */ if (dialog.accept && name.getText().length() > 0) { - for (int i = 0; i < sequences.length; i++) + for (int i = 0; i < sequences.size(); i++) { - SequenceFeature sf = features[i]; + SequenceFeature sf = features.get(i); SequenceFeature sf2 = new SequenceFeature(enteredType, enteredDesc, sf.getBegin(), sf.getEnd(), enteredGroup); ffile.parseDescriptionHTML(sf2, false); - sequences[i].addSequenceFeature(sf2); + sequences.get(i).addSequenceFeature(sf2); } Color newColour = colourPanel.getBackground(); diff --git a/src/jalview/appletgui/Finder.java b/src/jalview/appletgui/Finder.java index cd5030a..41e2a64 100644 --- a/src/jalview/appletgui/Finder.java +++ b/src/jalview/appletgui/Finder.java @@ -41,6 +41,8 @@ import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.List; import java.util.Vector; public class Finder extends Panel implements ActionListener @@ -113,19 +115,15 @@ public class Finder extends Panel implements ActionListener public void createNewGroup_actionPerformed() { - SequenceI[] seqs = new SequenceI[searchResults.getSize()]; - SequenceFeature[] features = new SequenceFeature[searchResults - .getSize()]; + List seqs = new ArrayList(); + List features = new ArrayList(); String searchString = textfield.getText().trim(); - int i = 0; for (SearchResultMatchI match : searchResults.getResults()) { - seqs[i] = match.getSequence().getDatasetSequence(); - - features[i] = new SequenceFeature(searchString, "Search Results", - match.getStart(), match.getEnd(), "Search Results"); - i++; + seqs.add(match.getSequence().getDatasetSequence()); + features.add(new SequenceFeature(searchString, "Search Results", + match.getStart(), match.getEnd(), "Search Results")); } if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs, diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index ab6dd9a..c57f88a 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -54,7 +54,9 @@ import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.util.Collections; import java.util.List; +import java.util.ListIterator; import java.util.Vector; public class SeqPanel extends Panel implements MouseMotionListener, @@ -564,8 +566,14 @@ public class SeqPanel extends Panel implements MouseMotionListener, av.setSelectionGroup(null); } + int column = findRes(evt); + boolean isGapped = Comparison.isGap(sequence.getCharAt(column)); List features = findFeaturesAtRes(sequence, - sequence.findPosition(findRes(evt))); + sequence.findPosition(column)); + if (isGapped) + { + removeAdjacentFeatures(features, column + 1, sequence); + } if (!features.isEmpty()) { @@ -573,11 +581,8 @@ public class SeqPanel extends Panel implements MouseMotionListener, highlight.addResult(sequence, features.get(0).getBegin(), features .get(0).getEnd()); seqCanvas.highlightSearchResults(highlight); - SequenceFeature[] featuresArray = features - .toArray(new SequenceFeature[features.size()]); seqCanvas.getFeatureRenderer().amendFeatures( - new SequenceI[] { sequence }, featuresArray, false, ap); - + Collections.singletonList(sequence), features, false, ap); seqCanvas.highlightSearchResults(null); } } @@ -802,9 +807,11 @@ public class SeqPanel extends Panel implements MouseMotionListener, } final char ch = sequence.getCharAt(column); - int respos = Comparison.isGap(ch) ? -1 : sequence.findPosition(column); + boolean isGapped = Comparison.isGap(ch); + // find residue at column (or nearest if at a gap) + int respos = sequence.findPosition(column); - if (ssm != null && respos != -1) + if (ssm != null && !isGapped) { mouseOverSequence(sequence, column, respos); } @@ -813,30 +820,21 @@ public class SeqPanel extends Panel implements MouseMotionListener, text.append("Sequence ").append(Integer.toString(seq + 1)) .append(" ID: ").append(sequence.getName()); - String obj = null; - if (respos != -1) + if (!isGapped) { if (av.getAlignment().isNucleotide()) { - obj = ResidueProperties.nucleotideName.get(ch); - if (obj != null) - { - text.append(" Nucleotide: ").append(obj); - } + String base = ResidueProperties.nucleotideName.get(ch); + text.append(" Nucleotide: ").append(base == null ? ch : base); } else { - obj = (ch == 'x' || ch == 'X') ? "X" : ResidueProperties.aa2Triplet + String residue = (ch == 'x' || ch == 'X') ? "X" + : ResidueProperties.aa2Triplet .get(String.valueOf(ch)); - if (obj != null) - { - text.append(" Residue: ").append(obj); - } - } - if (obj != null) - { - text.append(" (").append(Integer.toString(respos)).append(")"); + text.append(" Residue: ").append(residue == null ? ch : residue); } + text.append(" (").append(Integer.toString(respos)).append(")"); } ap.alignFrame.statusBar.setText(text.toString()); @@ -864,12 +862,17 @@ public class SeqPanel extends Panel implements MouseMotionListener, } /* - * add feature details to tooltip if over one or more features + * add feature details to tooltip, including any that straddle + * a gapped position */ - if (respos != -1) + if (av.isShowSequenceFeatures()) { List allFeatures = findFeaturesAtRes(sequence, respos); + if (isGapped) + { + removeAdjacentFeatures(allFeatures, column + 1, sequence); + } for (SequenceFeature sf : allFeatures) { tooltipText.append(sf.getType() + " " + sf.begin + ":" + sf.end); @@ -906,6 +909,32 @@ public class SeqPanel extends Panel implements MouseMotionListener, return seqCanvas.getFeatureRenderer().findFeaturesAtRes(sequence, res); } + /** + * Removes from the list of features any that start after, or end before, the + * given column position. This allows us to retain only those features + * adjacent to a gapped position that straddle the position. + * + * @param features + * @param column + * alignment column (1..) + * @param sequence + */ + protected void removeAdjacentFeatures(List features, + int column, SequenceI sequence) + { + // TODO should this be an AlignViewController method (shared by gui)? + ListIterator it = features.listIterator(); + while (it.hasNext()) + { + SequenceFeature sf = it.next(); + if (sequence.findIndex(sf.getBegin()) > column + || sequence.findIndex(sf.getEnd()) < column) + { + it.remove(); + } + } + } + Tooltip tooltip; /** @@ -1433,9 +1462,8 @@ public class SeqPanel extends Panel implements MouseMotionListener, sequence.findPosition(res)); Vector links = null; - for (int i = 0; i < allFeatures.size(); i++) + for (SequenceFeature sf : allFeatures) { - SequenceFeature sf = allFeatures.get(i); if (sf.links != null) { if (links == null) diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 494d690..81ddb8a 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -62,6 +62,7 @@ import java.awt.event.MouseWheelListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.ListIterator; import javax.swing.JPanel; import javax.swing.SwingUtilities; @@ -754,8 +755,9 @@ public class SeqPanel extends JPanel implements MouseListener, /* * set status bar message, returning residue position in sequence */ + boolean isGapped = Comparison.isGap(sequence.getCharAt(column)); final int pos = setStatusMessage(sequence, column, seq); - if (ssm != null && pos > -1) + if (ssm != null && !isGapped) { mouseOverSequence(sequence, column, pos); } @@ -784,10 +786,19 @@ public class SeqPanel extends JPanel implements MouseListener, } } - if (av.isShowSequenceFeatures() && pos != -1) + /* + * add any features at the position to the tooltip; if over a gap, only + * add features that straddle the gap (pos may be the residue before or + * after the gap) + */ + if (av.isShowSequenceFeatures()) { List features = ap.getFeatureRenderer() .findFeaturesAtRes(sequence.getDatasetSequence(), pos); + if (isGapped) + { + removeAdjacentFeatures(features, column + 1, sequence); + } seqARep.appendFeatures(tooltipText, pos, features, this.ap.getSeqPanel().seqCanvas.fr.getMinMax()); } @@ -801,14 +812,40 @@ public class SeqPanel extends JPanel implements MouseListener, String textString = tooltipText.toString(); if (lastTooltip == null || !lastTooltip.equals(textString)) { - String formatedTooltipText = JvSwingUtils.wrapTooltip(true, + String formattedTooltipText = JvSwingUtils.wrapTooltip(true, textString); - setToolTipText(formatedTooltipText); + setToolTipText(formattedTooltipText); lastTooltip = textString; } } } + /** + * Removes from the list of features any that start after, or end before, the + * given column position. This allows us to retain only those features + * adjacent to a gapped position that straddle the position. + * + * @param features + * @param column + * alignment column (1..) + * @param sequence + */ + protected void removeAdjacentFeatures(List features, + final int column, SequenceI sequence) + { + // TODO should this be an AlignViewController method (and reused by applet)? + ListIterator it = features.listIterator(); + while (it.hasNext()) + { + SequenceFeature sf = it.next(); + if (sequence.findIndex(sf.getBegin()) > column + || sequence.findIndex(sf.getEnd()) < column) + { + it.remove(); + } + } + } + private Point lastp = null; /* @@ -853,9 +890,10 @@ public class SeqPanel extends JPanel implements MouseListener, /** * Sets the status message in alignment panel, showing the sequence number - * (index) and id, residue and residue position for the given sequence and - * column position. Returns the calculated residue position in the sequence, - * or -1 for a gapped column position. + * (index) and id, and residue and residue position if not at a gap, for the + * given sequence and column position. Returns the residue position returned + * by Sequence.findPosition. Note this may be for the nearest adjacent residue + * if at a gapped position. * * @param sequence * aligned sequence object @@ -863,7 +901,8 @@ public class SeqPanel extends JPanel implements MouseListener, * alignment column * @param seq * index of sequence in alignment - * @return position of column in sequence or -1 if at a gap + * @return sequence position of residue at column, or adjacent residue if at a + * gap */ int setStatusMessage(SequenceI sequence, final int column, int seq) { @@ -877,36 +916,34 @@ public class SeqPanel extends JPanel implements MouseListener, .append(sequence.getName()); String residue = null; + /* * Try to translate the display character to residue name (null for gap). */ final String displayChar = String.valueOf(sequence.getCharAt(column)); - if (av.getAlignment().isNucleotide()) + boolean isGapped = Comparison.isGap(sequence.getCharAt(column)); + int pos = sequence.findPosition(column); + + if (!isGapped) { - residue = ResidueProperties.nucleotideName.get(displayChar); - if (residue != null) + boolean nucleotide = av.getAlignment().isNucleotide(); + if (nucleotide) { - text.append(" Nucleotide: ").append(residue); + residue = ResidueProperties.nucleotideName.get(displayChar); } - } - else - { - residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*" - .equals(displayChar) ? "STOP" : ResidueProperties.aa2Triplet - .get(displayChar)); - if (residue != null) + else { - text.append(" Residue: ").append(residue); + residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*" + .equals(displayChar) ? "STOP" + : ResidueProperties.aa2Triplet.get(displayChar)); } - } + text.append(" ").append(nucleotide ? "Nucleotide" : "Residue") + .append(": ").append(residue == null ? displayChar : residue); - int pos = -1; - if (residue != null) - { - pos = sequence.findPosition(column); text.append(" (").append(Integer.toString(pos)).append(")"); } ap.alignFrame.statusBar.setText(text.toString()); + return pos; } @@ -1546,9 +1583,20 @@ public class SeqPanel extends JPanel implements MouseListener, av.setSelectionGroup(null); } + int column = findColumn(evt); + boolean isGapped = Comparison.isGap(sequence.getCharAt(column)); + + /* + * find features at the position (if not gapped), or straddling + * the position (if at a gap) + */ List features = seqCanvas.getFeatureRenderer() .findFeaturesAtRes(sequence.getDatasetSequence(), - sequence.findPosition(findColumn(evt))); + sequence.findPosition(column)); + if (isGapped) + { + removeAdjacentFeatures(features, column, sequence); + } if (!features.isEmpty()) {