import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Hashtable;
+import java.util.List;
import java.util.Set;
import java.util.Vector;
import jalview.datamodel.Alignment;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenSequences;
import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResults;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.structure.SelectionSource;
import jalview.structure.StructureSelectionManager;
import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
+import jalview.util.MappingUtils;
import jalview.util.MessageManager;
import jalview.viewmodel.AlignmentViewport;
import jalview.ws.params.AutoCalcSetting;
{
this.gatherViewsHere = gatherViewsHere;
}
+
+ /**
+ * If this viewport has a (Protein/cDNA) complement, then scroll the
+ * complementary alignment to match this one.
+ *
+ * @param horizontal
+ * true for horizontal scroll event, false for vertical
+ */
+ public void scrollComplementaryAlignment(boolean horizontal)
+ {
+ /*
+ * If no complement, or it is not following scrolling, do nothing.
+ */
+ // TODO pull up followHighlight to AlignmentViewport/AlignViewportI
+ final AlignViewport codingComplement = (AlignViewport) getCodingComplement();
+ if (codingComplement == null || !codingComplement.followHighlight)
+ {
+ return;
+ }
+ boolean iAmProtein = !getAlignment().isNucleotide();
+ AlignmentI proteinAlignment = iAmProtein ? getAlignment()
+ : codingComplement.getAlignment();
+ if (proteinAlignment == null)
+ {
+ return;
+ }
+ final Set<AlignedCodonFrame> mappings = proteinAlignment
+ .getCodonFrames();
+
+ /*
+ * Heuristic: find the first mapped sequence (if any) with a non-gapped
+ * residue in the middle column of the visible region. Scroll the
+ * complementary alignment to line up the corresponding residue.
+ */
+ int seqOffset = 0;
+ SequenceI sequence = null;
+ int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+ final HiddenSequences hiddenSequences = getAlignment()
+ .getHiddenSequences();
+ for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++)
+ {
+ sequence = getAlignment().getSequenceAt(seqNo);
+ if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
+ {
+ continue;
+ }
+ if (Comparison.isGap(sequence.getCharAt(middleColumn)))
+ {
+ continue;
+ }
+ List<AlignedCodonFrame> seqMappings = MappingUtils
+ .findMappingsForSequence(sequence, mappings);
+ if (!seqMappings.isEmpty())
+ {
+ break;
+ }
+ }
+
+ if (sequence == null)
+ {
+ /*
+ * No ungapped mapped sequence in middle column - do nothing
+ */
+ return;
+ }
+ SearchResults sr = MappingUtils.buildSearchResults(sequence,
+ sequence.findPosition(middleColumn), mappings);
+ codingComplement.getAlignPanel().scrollAsComplement(sr, seqOffset,
+ horizontal);
+ }
}
*/
package jalview.gui;
-import jalview.analysis.AnnotationSorter;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceGroup;
-import jalview.datamodel.SequenceI;
-import jalview.jbgui.GAlignmentPanel;
-import jalview.math.AlignmentDimension;
-import jalview.schemes.ResidueProperties;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.MessageManager;
-
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
+import java.util.List;
import javax.swing.SwingUtilities;
+import jalview.analysis.AnnotationSorter;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.jbgui.GAlignmentPanel;
+import jalview.math.AlignmentDimension;
+import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+
/**
* DOCUMENT ME!
*
int vextent = 0;
+ /*
+ * Flag set while scrolling to follow complementary cDNA/protein scroll. When
+ * true, suppresses invoking the same method recursively.
+ */
+ private boolean followingComplementScroll;
+
/**
* Creates a new AlignmentPanel object.
*
}
/**
- * scroll the view to show the position of the highlighted region in results
+ * Scroll the view to show the position of the highlighted region in results
* (if any) and redraw the overview
*
* @param results
*/
public boolean scrollToPosition(SearchResults results)
{
- return scrollToPosition(results, true);
+ return scrollToPosition(results, true, false);
}
/**
- * scroll the view to show the position of the highlighted region in results
+ * Scroll the view to show the position of the highlighted region in results
+ * (if any)
+ *
+ * @param searchResults
+ * @param redrawOverview
+ * @return
+ */
+ public boolean scrollToPosition(SearchResults searchResults, boolean redrawOverview)
+ {
+ return scrollToPosition(searchResults, redrawOverview, false);
+ }
+
+ /**
+ * Scroll the view to show the position of the highlighted region in results
* (if any)
*
* @param results
* @param redrawOverview
* - when set, the overview will be recalculated (takes longer)
+ * @param centre
+ * if true, try to centre the search results horizontally in the view
* @return false if results were not found
*/
public boolean scrollToPosition(SearchResults results,
- boolean redrawOverview)
+ boolean redrawOverview, boolean centre)
{
- int startv, endv, starts, ends, width;
+ int startv, endv, starts, ends;
// TODO: properly locate search results in view when large numbers of hidden
// columns exist before highlighted region
// do we need to scroll the panel?
int end = r[1];
// System.err.println("Seq : "+seqIndex+" Scroll to "+start+","+end); //
// DEBUG
+
+ /*
+ * To centre results, scroll to positions half the visible width
+ * left/right of the start/end positions
+ */
+ if (centre)
+ {
+ int offset = (av.getEndRes() - av.getStartRes() + 1) / 2 - 1;
+ start = Math.max(start - offset, 0);
+ end = Math.min(end + offset, seq.getEnd() - 1);
+ }
if (start < 0)
{
return false;
{
if ((startv = av.getStartRes()) >= start)
{
+ /*
+ * Scroll left to make start of search results visible
+ */
setScrollValues(start - 1, seqIndex);
}
else if ((endv = av.getEndRes()) <= end)
{
+ /*
+ * Scroll right to make end of search results visible
+ */
setScrollValues(startv + 1 + end - endv, seqIndex);
}
else if ((starts = av.getStartSeq()) > seqIndex)
{
+ /*
+ * Scroll up to make start of search results visible
+ */
setScrollValues(av.getStartRes(), seqIndex);
}
else if ((ends = av.getEndSeq()) <= seqIndex)
{
+ /*
+ * Scroll down to make end of search results visible
+ */
setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);
}
+ /*
+ * Else results are already visible - no need to scroll
+ */
}
else
{
}
}
}
+ /*
+ * If there is one, scroll the (Protein/cDNA) complementary alignment to
+ * match, unless we are ourselves doing that.
+ */
+ if (isFollowingComplementScroll())
+ {
+ setFollowingComplementScroll(false);
+ }
+ else
+ {
+ av.scrollComplementaryAlignment(evt.getSource() == hscroll);
+ }
}
/**
{
this.idPanel = idPanel;
}
+
+ /**
+ * Follow a scrolling change in the (cDNA/Protein) complementary alignment.
+ * The aim is to keep the two alignments 'lined up' on their centre columns.
+ *
+ * @param sr
+ * holds mapped region(s) of this alignment that we are scrolling
+ * 'to'; may be modified for sequence offset by this method
+ * @param seqOffset
+ * the number of visible sequences to show above the mapped region
+ * @param horizontal
+ * if true, horizontal scrolling, else vertical
+ */
+ public void scrollAsComplement(SearchResults sr, int seqOffset,
+ boolean horizontal)
+ {
+ /*
+ * To avoid jumpy vertical scrolling (if some sequences are gapped or not
+ * mapped), we can make the scroll-to location a sequence above the one
+ * actually mapped.
+ */
+ SequenceI mappedTo = sr.getResultSequence(0);
+ List<SequenceI> seqs = av.getAlignment().getSequences();
+
+ /*
+ * This is like AlignmentI.findIndex(seq) but here we are matching the
+ * dataset sequence not the aligned sequence
+ */
+ int sequenceIndex = 0;
+ boolean matched = false;
+ for (SequenceI seq : seqs)
+ {
+ if (mappedTo == seq.getDatasetSequence())
+ {
+ matched = true;
+ break;
+ }
+ sequenceIndex++;
+ }
+ if (!matched)
+ {
+ return; // failsafe, shouldn't happen
+ }
+ sequenceIndex = Math.max(0, sequenceIndex - seqOffset);
+ sr.getResults().get(0)
+ .setSequence(av.getAlignment().getSequenceAt(sequenceIndex));
+
+ /*
+ * Scroll to position but centring the target residue. Also set a state flag
+ * to prevent adjustmentValueChanged performing this recursively.
+ */
+ setFollowingComplementScroll(true);
+ scrollToPosition(sr, true, true);
+ }
+
+ /**
+ * Set a flag to say we are scrolling to follow a (cDNA/protein) complement.
+ *
+ * @param b
+ */
+ protected void setFollowingComplementScroll(boolean b)
+ {
+ this.followingComplementScroll = b;
+ }
+
+ protected boolean isFollowingComplementScroll()
+ {
+ return this.followingComplementScroll;
+ }
}
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.junit.Test;
import jalview.datamodel.SearchResults.Match;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
import jalview.gui.AlignViewport;
import jalview.io.AppletFormatAdapter;
import jalview.io.FormatAdapter;
assertEquals(0, mappedGroup.getStartRes());
assertEquals(4, mappedGroup.getEndRes());
}
+
+ @Test
+ public void testFindMappingsForSequence()
+ {
+ SequenceI seq1 = new Sequence("Seq1", "ABC");
+ SequenceI seq2 = new Sequence("Seq2", "ABC");
+ SequenceI seq3 = new Sequence("Seq3", "ABC");
+ SequenceI seq4 = new Sequence("Seq4", "ABC");
+ seq1.createDatasetSequence();
+ seq2.createDatasetSequence();
+ seq3.createDatasetSequence();
+ seq4.createDatasetSequence();
+
+ /*
+ * Create mappings from seq1 to seq2, seq2 to seq1, seq3 to seq1
+ */
+ AlignedCodonFrame acf1 = new AlignedCodonFrame();
+ MapList map = new MapList(new int[]
+ { 1, 3 }, new int[]
+ { 1, 3 },1, 1);
+ acf1.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
+ AlignedCodonFrame acf2 = new AlignedCodonFrame();
+ acf2.addMap(seq2.getDatasetSequence(), seq1.getDatasetSequence(), map);
+ AlignedCodonFrame acf3 = new AlignedCodonFrame();
+ acf3.addMap(seq3.getDatasetSequence(), seq1.getDatasetSequence(), map);
+
+ Set<AlignedCodonFrame> mappings = new HashSet<AlignedCodonFrame>();
+ mappings.add(acf1);
+ mappings.add(acf2);
+ mappings.add(acf3);
+
+ /*
+ * Seq1 has three mappings
+ */
+ List<AlignedCodonFrame> result = MappingUtils.findMappingsForSequence(
+ seq1, mappings);
+ assertEquals(3, result.size());
+ assertTrue(result.contains(acf1));
+ assertTrue(result.contains(acf2));
+ assertTrue(result.contains(acf3));
+
+ /*
+ * Seq2 has two mappings
+ */
+ result = MappingUtils.findMappingsForSequence(seq2, mappings);
+ assertEquals(2, result.size());
+ assertTrue(result.contains(acf1));
+ assertTrue(result.contains(acf2));
+
+ /*
+ * Seq3 has one mapping
+ */
+ result = MappingUtils.findMappingsForSequence(seq3, mappings);
+ assertEquals(1, result.size());
+ assertTrue(result.contains(acf3));
+
+ /*
+ * Seq4 has no mappings
+ */
+ result = MappingUtils.findMappingsForSequence(seq4, mappings);
+ assertEquals(0, result.size());
+
+ result = MappingUtils.findMappingsForSequence(null, mappings);
+ assertEquals(0, result.size());
+
+ result = MappingUtils.findMappingsForSequence(seq1, null);
+ assertEquals(0, result.size());
+
+ result = MappingUtils.findMappingsForSequence(null, null);
+ assertEquals(0, result.size());
+}
}