From 41fe43633ee3cc43a45a356176689731ff73ba67 Mon Sep 17 00:00:00 2001 From: Jim Procter Date: Thu, 15 Sep 2022 16:18:30 +0100 Subject: [PATCH] JAL-4062 avc operation and model method for copying SearchResultsI.getMatchingSubSequences() to clipboard --- src/jalview/api/AlignViewControllerI.java | 12 +++++-- src/jalview/controller/AlignViewController.java | 37 +++++++++++++++++--- src/jalview/datamodel/SearchResults.java | 23 +++++++++++++ src/jalview/datamodel/SearchResultsI.java | 8 +++++ src/jalview/gui/Finder.java | 24 ++----------- test/jalview/datamodel/SearchResultsTest.java | 41 +++++++++++++++++++++++ 6 files changed, 117 insertions(+), 28 deletions(-) diff --git a/src/jalview/api/AlignViewControllerI.java b/src/jalview/api/AlignViewControllerI.java index 58a58e9..3e689d1 100644 --- a/src/jalview/api/AlignViewControllerI.java +++ b/src/jalview/api/AlignViewControllerI.java @@ -20,10 +20,10 @@ */ package jalview.api; -import jalview.io.DataSourceType; - import java.util.List; +import jalview.io.DataSourceType; + /** * prototype abstract controller for a Jalview alignment view * @@ -111,4 +111,12 @@ public interface AlignViewControllerI boolean markHighlightedColumns(boolean invert, boolean extendCurrent, boolean toggle); + /** + * copies each distinct highlighted region on the current view as a new + * sequence on the clipboard + * + * @return + */ + boolean copyHighlightedRegionsToClipboard(); + } diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java index 2fb9cdd..4434331 100644 --- a/src/jalview/controller/AlignViewController.java +++ b/src/jalview/controller/AlignViewController.java @@ -20,6 +20,10 @@ */ package jalview.controller; +import java.awt.Color; +import java.util.BitSet; +import java.util.List; + import jalview.analysis.AlignmentSorter; import jalview.api.AlignViewControllerGuiI; import jalview.api.AlignViewControllerI; @@ -29,19 +33,17 @@ import jalview.api.FeatureRenderer; import jalview.commands.OrderCommand; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.SearchResultsI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.gui.Desktop; import jalview.io.DataSourceType; import jalview.io.FeaturesFile; import jalview.schemes.ColourSchemeI; import jalview.util.MessageManager; -import java.awt.Color; -import java.util.BitSet; -import java.util.List; - public class AlignViewController implements AlignViewControllerI { AlignViewportI viewport = null; @@ -471,4 +473,31 @@ public class AlignViewController implements AlignViewControllerI return false; } + @Override + public boolean copyHighlightedRegionsToClipboard() + { + if (!viewport.hasSearchResults()) + { + // do nothing if no selection exists + return false; + } + + SearchResultsI searchResults = viewport.getSearchResults(); + if (searchResults.isEmpty()) + { + return false; // shouldn't happen + } + List seqs = searchResults.getMatchingSubSequences(); + + // TODO: pass in hiddenColumns according to intersection of searchResults + // and visible columns. Currently this isn't done, since each contig becomes + // a single subsequence + Desktop.jalviewClipboard = new Object[] { + seqs.toArray(new SequenceI[0]), + alignPanel.getAlignment().getDataset(), null }; + avcg.setStatus(MessageManager.formatMessage( + "label.copied_sequences_to_clipboard", seqs.size())); + // Technically we should return false, since view has not changed + return false; + } } diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java index 880f970..909a0fe 100755 --- a/src/jalview/datamodel/SearchResults.java +++ b/src/jalview/datamodel/SearchResults.java @@ -370,4 +370,27 @@ public class SearchResults implements SearchResultsI { matches.addAll(toAdd.getResults()); } + + @Override + public List getMatchingSubSequences() + { + List seqs = new ArrayList<>(); + + /* + * assemble dataset sequences, and template new sequence features, + * for the amend features dialog + */ + for (SearchResultMatchI match : matches) + { + SequenceI seq = match.getSequence(); + while (seq.getDatasetSequence() != null) + { + seq = seq.getDatasetSequence(); + } + // getSubSequence is index-base0, findIndex returns index-base1 + seqs.add(seq.getSubSequence(seq.findIndex(match.getStart()) - 1, + seq.findIndex(match.getEnd()))); + } + return seqs; + } } diff --git a/src/jalview/datamodel/SearchResultsI.java b/src/jalview/datamodel/SearchResultsI.java index c89f363..7946824 100644 --- a/src/jalview/datamodel/SearchResultsI.java +++ b/src/jalview/datamodel/SearchResultsI.java @@ -118,4 +118,12 @@ public interface SearchResultsI * @return number of bits set */ int markColumns(SequenceCollectionI sqcol, BitSet bs); + + /** + * Return sub-sequences corresponding to distinct contiguous ranges in the + * matching set + * + * @return list of sequence objects + */ + List getMatchingSubSequences(); } \ No newline at end of file diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java index 6dacbd8..6527cdc 100755 --- a/src/jalview/gui/Finder.java +++ b/src/jalview/gui/Finder.java @@ -260,28 +260,8 @@ public class Finder extends GFinder { return; // shouldn't happen } - List seqs = new ArrayList<>(); - - String searchString = searchBox.getUserInput(); - String desc = "Search Results"; - - /* - * assemble dataset sequences, and template new sequence features, - * for the amend features dialog - */ - for (SearchResultMatchI match : searchResults.getResults()) - { - SequenceI seq = match.getSequence(); - while (seq.getDatasetSequence() != null) - { - seq = seq.getDatasetSequence(); - } - seqs.add(seq.getSubSequence(seq.findIndex(match.getStart()), - seq.findIndex(match.getEnd()) + 1)); - } - Desktop.jalviewClipboard = new Object[] { - seqs.toArray(new SequenceI[0]), ap.av.getAlignment().getDataset(), - ap.av.getAlignment().getHiddenColumns() }; + // assume viewport controller has same searchResults as we do... + ap.alignFrame.avc.copyHighlightedRegionsToClipboard(); } /** diff --git a/test/jalview/datamodel/SearchResultsTest.java b/test/jalview/datamodel/SearchResultsTest.java index 5d2b8a4..b1bb43c 100644 --- a/test/jalview/datamodel/SearchResultsTest.java +++ b/test/jalview/datamodel/SearchResultsTest.java @@ -26,6 +26,7 @@ import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; import java.util.BitSet; +import java.util.List; import org.junit.Assert; import org.testng.annotations.BeforeClass; @@ -369,4 +370,44 @@ public class SearchResultsTest sr.addResult(cds2, 7, 9); // start-end overlap assertTrue(sr.involvesSequence(cds2)); } + + /** + * Test extraction of Sequence objects for matched ranges on a sequence + */ + @Test(groups = { "Functional" }) + public void testGetSequences() + { + SequenceI seq1 = new Sequence("", "abcdefghijklm"); + SequenceI seq2 = new Sequence("", "nopqrstuvwxyz"); + seq2.setStart(23); + seq2.setEnd(35); + List seqres = null; + + SearchResultsI sr = new SearchResults(); + seqres = sr.getMatchingSubSequences(); + assertEquals(0, seqres.size()); + + sr.addResult(seq1, 3, 5); + seqres = sr.getMatchingSubSequences(); + + assertEquals(1, seqres.size()); + assertEquals("cde", seqres.get(0).getSequenceAsString()); + assertEquals(3, seqres.get(0).getStart()); + assertEquals(seq1, seqres.get(0).getDatasetSequence()); + + sr.addResult(seq1, 3, 6); + seqres = sr.getMatchingSubSequences(); + + assertEquals(2, seqres.size()); + assertEquals("cdef", seqres.get(1).getSequenceAsString()); + assertEquals(3, seqres.get(1).getStart()); + + // this is a quirk - match on 26-29 yields subsequence 27-30 + sr.addResult(seq2, 26, 29); + seqres = sr.getMatchingSubSequences(); + assertEquals(3, seqres.size()); + assertEquals("qrst", seqres.get(2).getSequenceAsString()); + assertEquals(26, seqres.get(2).getStart()); + } + } -- 1.7.10.2