Merge branch 'bug/JAL-2277_refactorPrintUnwrapped' into develop
authortcofoegbu <tcnofoegbu@dundee.ac.uk>
Tue, 22 Nov 2016 17:21:16 +0000 (17:21 +0000)
committertcofoegbu <tcnofoegbu@dundee.ac.uk>
Tue, 22 Nov 2016 17:21:16 +0000 (17:21 +0000)
47 files changed:
help/html/features/chimera.html
help/html/features/clarguments.html
help/html/features/jmol.html
help/html/features/search.html
help/html/keys.html
help/html/menus/alignmentMenu.html
help/html/menus/alwselect.html
help/html/releases.html
resources/lang/Messages.properties
src/jalview/analysis/Finder.java
src/jalview/api/AlignViewControllerI.java
src/jalview/api/AlignViewportI.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/Finder.java
src/jalview/appletgui/SeqCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/AlignedCodonFrame.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/SearchResultMatchI.java [new file with mode: 0644]
src/jalview/datamodel/SearchResults.java
src/jalview/datamodel/SearchResultsI.java [new file with mode: 0644]
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/Finder.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/io/BioJsHTMLOutput.java
src/jalview/io/HTMLOutput.java
src/jalview/io/HtmlSvgOutput.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/structure/SequenceListener.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/util/MappingUtils.java
src/jalview/viewmodel/AlignmentViewport.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/FinderTest.java [new file with mode: 0644]
test/jalview/controller/AlignViewControllerTest.java
test/jalview/datamodel/MatchTest.java
test/jalview/datamodel/SearchResultsTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/util/MappingUtilsTest.java

index 1b0b9c1..cbef2c1 100644 (file)
     number and chain code ([RES]Num:Chain). Moving the mouse over an
     associated residue in an alignment window highlights the associated
     atoms in the displayed structures. When residues are selected in the
-    Chimera window, they are highlighted on the alignment. For
-    comprehensive details of Chimera's commands, refer to the tool's
-    Help menu.
+    Chimera window, they are highlighted on the alignment.
+  <p>For comprehensive details of Chimera's commands, refer to the
+    tool's Help menu.</p>
+  <p>
+    <strong>Selecting residues in Jalview from Chimera</strong><br />
+    When a selection is highlighted in a Jalview window, use the
+    <em>Select&#8594;Select Highlighted Region</em> or press <em>B</em>
+    to add the mapped positions to the alignment window's column
+    selection.
+  </p>
   <p>
     Basic screen operations (see <a
       href="http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/mouse.html">Chimera
index 4b4aab9..e065494 100644 (file)
       <td><div align="left">Create Scalable Vector Graphics
           file FILE from alignment.</div></td>
     </tr>
+    <tr>
+      <td><div align="center">-biojsMSA FILE</div></td>
+      <td><div align="left">Write an HTML page to display
+          the alignment with the <a href="biojsmsa.html">
+          BioJS MSAviewer MSA</a>
+          </div>
+      </td>
+    </tr>
   </table>
 </body>
 </html>
index 2f10196..0cd6168 100644 (file)
       based structure superposition was added in Jalview 2.6</em>
   </p>
   <p>
-    <strong>Controls</strong><br> The structure is by default
-    rendered as a ribbon diagram. Moving the mouse over the structure
-    brings up tooltips giving the residue name, PDB residue number and
-    chain code, atom name and number
-    ([RES]Num:Chain.AtomName#AtomNumber). If a mapping exists to a
-    residue in any associated sequences, then this will be highlighted
-    in each one's alignment window. The converse also occurs - moving
-    the mouse over an associated residue in an alignment window
-    highlights the associated atoms in the displayed structures.
+    <strong>Controls</strong><br> The structure is by default rendered
+    as a ribbon diagram. Moving the mouse over the structure brings up
+    tooltips giving the residue name, PDB residue number and chain code,
+    atom name and number ([RES]Num:Chain.AtomName#AtomNumber). If a
+    mapping exists to a residue in any associated sequences, then this
+    will be highlighted in each one's alignment window. The converse
+    also occurs - moving the mouse over an associated residue in an
+    alignment window highlights the associated atoms in the displayed
+    structures. Press B or use
+    <em>Select&#8594;Select Highlighted columns</em> from any linked
+    alignment window to mark the columns highlighted after mousing over
+    the structure.
   </p>
   <p>Selecting a residue highlights its associated sequence residue
     and alpha carbon location. Double clicking an atom allows distances
index 796d623..a8238eb 100755 (executable)
@@ -65,6 +65,17 @@ td {
     Settings&quot; under the &quot;View&quot; menu to change the
     visibility and colour of the new sequence feature.</p>
   <p>
+  <p>
+    <strong>Selecting regions from Search Results</strong>
+  </p>
+  <p>
+    Press 'B' or select the <em>Select Highlighted Columns</em> option
+    from the alignment window's select menu to add columns containing
+    highlighted search results to the alignment window's column
+    selection.
+  </p>
+  <p>
+  
     <strong>A quick Regular Expression Guide</strong>
   </p>
   <p>A regular expression is not just a simple text query - although
index 2ba9a49..b79ce4d 100755 (executable)
@@ -167,6 +167,19 @@ columns are selected, you should use the <a href="features/hiddenRegions.html">H
       <td>Both</td>
       <td>Launches the search window</td>
     </tr>
+    <tr><td><strong>B</strong></td>
+      <td>Both</td>
+      <td>Mark the currently highlighted columns</td>
+    </tr>
+    <tr><td><strong>Alt 'B'</strong></td>
+      <td>Both</td>
+      <td>Mark all but the currently highlighted columns</td>
+    </tr>
+    <tr><td><strong>Control 'B'</strong></td>
+      <td>Both</td>
+      <td>Toggle the marks on the currently highlighted 
+          columns (or all others if Alt is pressed)</td>
+    </tr>
     <tr>
       <td><strong>H</strong></td>
       <td>Both</td>
index c8b2270..167cb25 100755 (executable)
               Columns by Annotation</a></strong> <br /> <em>Select or Hide
             columns in the alignment according to secondary structure,
             labels and values shown in alignment annotation rows. </em></li>
+        <li><strong>Select Highlighted Columns</strong> <br /> <em>Selects
+        the columns currently highlighted as a result of a find, mouse
+        over, or selection event from a linked structure viewer or other
+        application. Modifiers will work on some platforms: ALT will add
+        all but the highlighted set to the column selection, and CTRL
+        (or META) will toggle the selection. </em></li>
       </ul></li>
     <li><strong>View</strong>
       <ul>
index b93f85b..07828a3 100644 (file)
           Columns by Annotation</a></strong> <br /> <em>Select or Hide columns
         in the alignment according to secondary structure, labels and
         values shown in alignment annotation rows. </em></li>
+    <li><strong>Select Highlighted Columns</strong> <br /> <em>Selects
+        the columns currently highlighted as a result of a find, mouse
+        over, or selection event from a linked structure viewer or other
+        application. Modifiers will work on some platforms: ALT will add
+        all but the highlighted set to the column selection, and CTRL
+        (or META) will toggle the selection. </em></li>
   </ul>
 </body>
 </html>
index 140466b..2d0c4e8 100755 (executable)
@@ -48,7 +48,7 @@
       <td width="60" nowrap>
         <div align="center">
           <strong><a name="Jalview.2.10.1">2.10.1</a><br />
-            <em>22/11/2016</em></strong>
+            <em>24/11/2016</em></strong>
         </div>
       </td>
       <td><div align="left">
            <li><!-- JAL-2272-->CTRL-Click on a selected region to open the alignment area popup menu doesn't work on El-Capitan</li>
            <li><!-- JAL-2280 -->Jalview doesn't offer to associate mmCIF files with similarly named sequences if dropped onto the alignment</li>
            <li><!-- JAL-2312 -->Additional mappings are shown for PDB entries where more chains exist in the PDB accession than are reported in the SIFTS file</li>
-           <li><!-- --></li>
-           <li><!-- --></li>
+           <li><!-- JAL-2317-->Certain structures do not get mapped to the structure view when displayed with Chimera</li>
+           <li><!-- JAL-2317-->No chains shown in the Chimera view panel's View->Show Chains submenu</li>
+      
+      <li><!-- --></li>
            <li><!-- --></li>
           </ul>
           <em>Applet</em>
               <!-- JAL-1887 -->Incorrect start and end reported for PDB
               to sequence mapping in 'View Mappings' report
             </li>
+            <li>
+              <!-- JAL-2284 -->Unable to read old Jalview projects that
+              contain non-XML data added after Jalvew wrote project.
+            </li>
+            <li><!-- JAL-2118 -->Newly created annotation row reorders
+              after clicking on it to create new annotation for a
+              column.
+            </li>
             <!--  may exclude, this is an external service stability issue  JAL-1941 
             -- > RNA 3D structure not added via DSSR service</li> -->
           </ul>
         </ul> <em>Applet</em>
         <ul>
           <li>Split frame example added to applet examples page</li>
+        </ul><em>Build and Deployment</em>
+        <ul>
+          <li><!--  JAL-1888 -->New ant target for running Jalview's test suite</li>
         </ul></td>
       <td>
         <div align="left">
index 01ba0ff..9a58965 100644 (file)
@@ -125,6 +125,8 @@ action.change_font_tree_panel = Change Font (Tree Panel)
 action.colour = Colour
 action.calculate = Calculate
 action.select_all = Select all
+action.select_highlighted_columns = Select Highlighted Columns
+tooltip.select_highlighted_columns = Press B to mark highlighted columns, Ctrl-B to toggle, and Alt-B to mark all but highlighted columns 
 action.deselect_all = Deselect all
 action.invert_selection = Invert selection
 action.using_jmol = Using Jmol
index 72097e0..25ee7d2 100644 (file)
 package jalview.analysis;
 
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.Sequence;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Vector;
 
+import com.stevesoft.pat.Regex;
+
 public class Finder
 {
   /**
    * Implements the search algorithms for the Find dialog box.
    */
-  SearchResults searchResults;
+  SearchResultsI searchResults;
 
   AlignmentI alignment;
 
-  jalview.datamodel.SequenceGroup selection = null;
+  SequenceGroup selection = null;
 
-  Vector idMatch = null;
+  Vector<SequenceI> idMatch = null;
 
   boolean caseSensitive = false;
 
@@ -46,10 +53,10 @@ public class Finder
 
   boolean findAll = false;
 
-  com.stevesoft.pat.Regex regex = null;
+  Regex regex = null;
 
   /**
-   * hold's last-searched position between calles to find(false)
+   * holds last-searched position between calls to find(false)
    */
   int seqIndex = 0, resIndex = -1;
 
@@ -83,11 +90,10 @@ public class Finder
     {
       searchString = searchString.toUpperCase();
     }
-    regex = new com.stevesoft.pat.Regex(searchString);
+    regex = new Regex(searchString);
     regex.setIgnoreCase(!caseSensitive);
     searchResults = new SearchResults();
-    idMatch = new Vector();
-    Sequence seq;
+    idMatch = new Vector<SequenceI>();
     String item = null;
     boolean found = false;
     int end = alignment.getHeight();
@@ -102,10 +108,11 @@ public class Finder
         selection = null;
       }
     }
+    SearchResultMatchI lastm = null;
 
     while (!found && (seqIndex < end))
     {
-      seq = (Sequence) alignment.getSequenceAt(seqIndex);
+      SequenceI seq = alignment.getSequenceAt(seqIndex);
 
       if ((selection != null && selection.getSize() > 0)
               && !selection.getSequences(null).contains(seq))
@@ -140,7 +147,7 @@ public class Finder
         {
         }
 
-        if (regex.search(seq.getName()))
+        if (regex.search(seq.getName()) && !idMatch.contains(seq))
         {
           idMatch.addElement(seq);
           hasResults = true;
@@ -153,7 +160,8 @@ public class Finder
         }
 
         if (isIncludeDescription() && seq.getDescription() != null
-                && regex.search(seq.getDescription()))
+                && regex.search(seq.getDescription())
+                && !idMatch.contains(seq))
         {
           idMatch.addElement(seq);
           hasResults = true;
@@ -174,16 +182,16 @@ public class Finder
       }
 
       // /Shall we ignore gaps???? - JBPNote: Add Flag for forcing this or not
-      StringBuffer noGapsSB = new StringBuffer();
+      StringBuilder noGapsSB = new StringBuilder();
       int insertCount = 0;
-      Vector spaces = new Vector();
+      List<Integer> spaces = new ArrayList<Integer>();
 
       for (int j = 0; j < item.length(); j++)
       {
-        if (!jalview.util.Comparison.isGap(item.charAt(j)))
+        if (!Comparison.isGap(item.charAt(j)))
         {
           noGapsSB.append(item.charAt(j));
-          spaces.addElement(new Integer(insertCount));
+          spaces.add(Integer.valueOf(insertCount));
         }
         else
         {
@@ -192,7 +200,6 @@ public class Finder
       }
 
       String noGaps = noGapsSB.toString();
-
       for (int r = resIndex; r < noGaps.length(); r++)
       {
 
@@ -201,22 +208,22 @@ public class Finder
           resIndex = regex.matchedFrom();
 
           if ((selection != null && selection.getSize() > 0)
-                  && ((resIndex + Integer.parseInt(spaces.elementAt(
-                          resIndex).toString())) < selection.getStartRes()))
+                  && (resIndex + spaces.get(resIndex) < selection
+                          .getStartRes()))
           {
             continue;
           }
           // if invalid string used, then regex has no matched to/from
-          int sres = seq
-                  .findPosition(resIndex
-                          + Integer.parseInt(spaces.elementAt(resIndex)
-                                  .toString()));
-          int eres = seq.findPosition(regex.matchedTo()
-                  - 1
-                  + Integer.parseInt(spaces
-                          .elementAt(regex.matchedTo() - 1).toString()));
-
-          searchResults.addResult(seq, sres, eres);
+          int sres = seq.findPosition(resIndex + spaces.get(resIndex));
+          int eres = seq.findPosition(regex.matchedTo() - 1
+                  + (spaces.get(regex.matchedTo() - 1)));
+          // only add result if not contained in previous result
+          if (lastm == null
+                  || (lastm.getSequence() != seq || (!(lastm.getStart() <= sres && lastm
+                          .getEnd() >= eres))))
+          {
+            lastm = searchResults.addResult(seq, sres, eres);
+          }
           hasResults = true;
           if (!findAll)
           {
@@ -320,9 +327,12 @@ public class Finder
   }
 
   /**
-   * @return the idMatch
+   * Returns the (possibly empty) list of matching sequences (when search
+   * includes searching sequence names)
+   * 
+   * @return
    */
-  public Vector getIdMatch()
+  public Vector<SequenceI> getIdMatch()
   {
     return idMatch;
   }
@@ -338,7 +348,7 @@ public class Finder
   /**
    * @return the searchResults
    */
-  public SearchResults getSearchResults()
+  public SearchResultsI getSearchResults()
   {
     return searchResults;
   }
index 26966ba..8ed2c95 100644 (file)
@@ -97,4 +97,16 @@ public interface AlignViewControllerI
   public boolean parseFeaturesFile(String file, String protocol,
           boolean relaxedIdMatching);
 
+  /**
+   * mark columns containing highlighted regions (e.g. from search, structure
+   * highlight, or a mouse over event in another viewer)
+   * 
+   * @param invert
+   * @param extendCurrent
+   * @param toggle
+   * @return
+   */
+  boolean markHighlightedColumns(boolean invert, boolean extendCurrent,
+          boolean toggle);
+
 }
index 8b80531..72542b3 100644 (file)
@@ -27,6 +27,7 @@ import jalview.datamodel.AlignmentView;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.ProfilesI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -423,4 +424,25 @@ public interface AlignViewportI extends ViewStyleI
    * @return true if group is defined on the alignment
    */
   boolean isSelectionDefinedGroup();
+
+  /**
+   * 
+   * @return true if there are search results on the view
+   */
+  boolean hasSearchResults();
+
+  /**
+   * set the search results for the view
+   * 
+   * @param results
+   *          - or null to clear current results
+   */
+  void setSearchResults(SearchResultsI results);
+
+  /**
+   * get search results for this view (if any)
+   * 
+   * @return search results or null
+   */
+  SearchResultsI getSearchResults();
 }
index e5178cb..4bd77b6 100644 (file)
@@ -28,6 +28,7 @@ import jalview.commands.CommandI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -433,7 +434,7 @@ public class AlignViewport extends AlignmentViewport implements
      * there is no complement, or it is not following highlights, or no mapping
      * is found, the result will be empty.
      */
-    SearchResults sr = new SearchResults();
+    SearchResultsI sr = new SearchResults();
     int seqOffset = findComplementScrollTarget(sr);
     if (!sr.isEmpty())
     {
index 813ab84..e97c347 100644 (file)
@@ -25,7 +25,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.JalviewLite;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
 import jalview.structure.StructureSelectionManager;
 
@@ -293,7 +293,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    * Highlight the given results on the alignment.
    * 
    */
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
     scrollToPosition(results);
     seqPanel.seqCanvas.highlightSearchResults(results);
@@ -306,7 +306,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    * @param results
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results)
+  public boolean scrollToPosition(SearchResultsI results)
   {
     return scrollToPosition(results, true);
   }
@@ -320,10 +320,10 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    *          - when set, the overview will be recalculated (takes longer)
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results,
+  public boolean scrollToPosition(SearchResultsI results,
           boolean redrawOverview)
   {
-    return scrollToPosition(results, redrawOverview, false);
+    return scrollToPosition(results, 0, redrawOverview, false);
   }
 
   /**
@@ -335,7 +335,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    *          - when set, the overview will be recalculated (takes longer)
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results,
+  public boolean scrollToPosition(SearchResultsI results,
+          int verticalOffset,
           boolean redrawOverview, boolean centre)
   {
     // do we need to scroll the panel?
@@ -347,6 +348,10 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       {
         return false;
       }
+      /*
+       * allow for offset of target sequence (actually scroll to one above it)
+       */
+
       SequenceI seq = alignment.getSequenceAt(seqIndex);
       int[] r = results.getResults(seq, 0, alignment.getWidth());
       if (r == null)
@@ -391,6 +396,11 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       {
         return false;
       }
+
+      /*
+       * allow for offset of target sequence (actually scroll to one above it)
+       */
+      seqIndex = Math.max(0, seqIndex - verticalOffset);
       return scrollTo(start, end, seqIndex, false, redrawOverview);
     }
     return true;
@@ -419,6 +429,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     {
       start = ostart;
     }
+
     if (!av.getWrapAlignment())
     {
       /*
@@ -902,14 +913,14 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    * @param seqOffset
    *          the number of visible sequences to show above the mapped region
    */
-  protected void scrollToCentre(SearchResults sr, int seqOffset)
+  protected void scrollToCentre(SearchResultsI sr, int seqOffset)
   {
     /*
      * 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);
+    SequenceI mappedTo = sr.getResults().get(0).getSequence();
     List<SequenceI> seqs = av.getAlignment().getSequences();
 
     /*
@@ -931,16 +942,14 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     {
       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);
+    // this should be scrollToPosition(sr,verticalOffset,
+    scrollToPosition(sr, seqOffset, true, true);
   }
 
   private void sendViewPosition()
index 82736d7..2fca07d 100644 (file)
@@ -22,6 +22,7 @@ package jalview.appletgui;
 
 import jalview.api.FeatureColourI;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.io.FeaturesFile;
@@ -225,7 +226,7 @@ public class FeatureRenderer extends
             start.setText(features[index].getBegin() + "");
             end.setText(features[index].getEnd() + "");
 
-            SearchResults highlight = new SearchResults();
+            SearchResultsI highlight = new SearchResults();
             highlight.addResult(sequences[0], features[index].getBegin(),
                     features[index].getEnd());
 
index 75d9b9e..d2fe69c 100644 (file)
@@ -20,7 +20,8 @@
  */
 package jalview.appletgui;
 
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
@@ -50,7 +51,7 @@ public class Finder extends Panel implements ActionListener
 
   Frame frame;
 
-  SearchResults searchResults;
+  SearchResultsI searchResults;
 
   int seqIndex = 0;
 
@@ -76,6 +77,7 @@ public class Finder extends Panel implements ActionListener
     frame.repaint();
     frame.addWindowListener(new WindowAdapter()
     {
+      @Override
       public void windowClosing(WindowEvent evt)
       {
         ap.highlightSearchResults(null);
@@ -84,6 +86,7 @@ public class Finder extends Panel implements ActionListener
     textfield.requestFocus();
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == textfield)
@@ -114,13 +117,15 @@ public class Finder extends Panel implements ActionListener
     SequenceFeature[] features = new SequenceFeature[searchResults
             .getSize()];
 
-    for (int i = 0; i < searchResults.getSize(); i++)
+    int i = 0;
+    for (SearchResultMatchI match : searchResults.getResults())
     {
-      seqs[i] = searchResults.getResultSequence(i);
+      seqs[i] = match.getSequence().getDatasetSequence();
 
       features[i] = new SequenceFeature(textfield.getText().trim(),
-              "Search Results", null, searchResults.getResultStart(i),
-              searchResults.getResultEnd(i), "Search Results");
+              "Search Results", null, match.getStart(), match.getEnd(),
+              "Search Results");
+      i++;
     }
 
     if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs,
@@ -152,7 +157,7 @@ public class Finder extends Panel implements ActionListener
     seqIndex = finder.getSeqIndex();
     resIndex = finder.getResIndex();
     searchResults = finder.getSearchResults();
-    Vector idMatch = finder.getIdMatch();
+    Vector<SequenceI> idMatch = finder.getIdMatch();
     boolean haveResults = false;
     // set or reset the GUI
     if ((idMatch.size() > 0))
@@ -246,6 +251,7 @@ public class Finder extends Panel implements ActionListener
     textfield.setBounds(new Rectangle(40, 17, 133, 21));
     textfield.addKeyListener(new java.awt.event.KeyAdapter()
     {
+      @Override
       public void keyTyped(KeyEvent e)
       {
         textfield_keyTyped(e);
index 7216bfe..5d6bb07 100755 (executable)
@@ -21,7 +21,7 @@
 package jalview.appletgui;
 
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.ScaleRenderer;
@@ -50,8 +50,6 @@ public class SeqCanvas extends Panel
 
   AlignViewport av;
 
-  SearchResults searchResults = null;
-
   boolean fastPaint = false;
 
   int cursorX = 0;
@@ -632,9 +630,10 @@ public class SeqCanvas extends Panel
 
       // / Highlight search Results once all sequences have been drawn
       // ////////////////////////////////////////////////////////
-      if (searchResults != null)
+      if (av.hasSearchResults())
       {
-        int[] visibleResults = searchResults.getResults(nextSeq, startRes,
+        int[] visibleResults = av.getSearchResults().getResults(nextSeq,
+                startRes,
                 endRes);
         if (visibleResults != null)
         {
@@ -843,10 +842,9 @@ public class SeqCanvas extends Panel
     }
   }
 
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
-    searchResults = results;
-
+    av.setSearchResults(results);
     repaint();
   }
 
index 6ca9499..8d6e683 100644 (file)
@@ -25,8 +25,9 @@ import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -458,7 +459,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
    * @param results
    * @return true if results were matched, false if not
    */
-  private boolean setStatusMessage(SearchResults results)
+  private boolean setStatusMessage(SearchResultsI results)
   {
     AlignmentI al = this.av.getAlignment();
     int sequenceIndex = al.findIndex(results);
@@ -467,7 +468,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       return false;
     }
     SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
-    for (Match m : results.getResults())
+    for (SearchResultMatchI m : results.getResults())
     {
       SequenceI seq = m.getSequence();
       if (seq.getDatasetSequence() != null)
@@ -559,7 +560,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
       if (features != null && features.length > 0)
       {
-        SearchResults highlight = new SearchResults();
+        SearchResultsI highlight = new SearchResults();
         highlight.addResult(sequence, features[0].getBegin(),
                 features[0].getEnd());
         seqCanvas.highlightSearchResults(highlight);
@@ -731,7 +732,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   }
 
   @Override
-  public void highlightSequence(SearchResults results)
+  public void highlightSequence(SearchResultsI results)
   {
     if (av.isFollowHighlight())
     {
index f508bc3..9451c3b 100644 (file)
@@ -381,4 +381,66 @@ public class AlignViewController implements AlignViewControllerI
     return featuresFile;
 
   }
+
+  @Override
+  public boolean markHighlightedColumns(boolean invert,
+          boolean extendCurrent, boolean toggle)
+  {
+    if (!viewport.hasSearchResults())
+    {
+      // do nothing if no selection exists
+      return false;
+    }
+    // JBPNote this routine could also mark rows, not just columns.
+    BitSet bs = new BitSet();
+    SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport
+            .getAlignment() : viewport.getSelectionGroup();
+
+    // this could be a lambda... - the remains of the method is boilerplate,
+    // except for the different messages for reporting selection.
+    int nseq = viewport.getSearchResults().markColumns(sqcol, bs);
+
+    ColumnSelection cs = viewport.getColumnSelection();
+    if (cs == null)
+    {
+      cs = new ColumnSelection();
+    }
+
+    if (bs.cardinality() > 0 || invert)
+    {
+      boolean changed = cs.markColumns(bs, sqcol.getStartRes(),
+              sqcol.getEndRes(), invert, extendCurrent, toggle);
+      if (changed)
+      {
+        viewport.setColumnSelection(cs);
+        alignPanel.paintAlignment(true);
+        int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
+                - bs.cardinality()
+                : bs.cardinality();
+        avcg.setStatus(MessageManager.formatMessage(
+                "label.view_controller_toggled_marked",
+                new String[] {
+                    toggle ? MessageManager.getString("label.toggled")
+                            : MessageManager.getString("label.marked"),
+                    String.valueOf(columnCount),
+                    invert ? MessageManager
+                            .getString("label.not_containing")
+                            : MessageManager.getString("label.containing"),
+                    "Highlight", Integer.valueOf(nseq).toString() }));
+        return true;
+      }
+    }
+    else
+    {
+      avcg.setStatus(MessageManager
+              .formatMessage("No highlighted regions marked"));
+      if (!extendCurrent)
+      {
+        cs.clear();
+        alignPanel.paintAlignment(true);
+      }
+    }
+    return false;
+  }
+
 }
index c5204eb..4fbfd62 100644 (file)
@@ -301,7 +301,7 @@ public class AlignedCodonFrame
    *          where highlighted regions go
    */
   public void markMappedRegion(SequenceI seq, int index,
-          SearchResults results)
+          SearchResultsI results)
   {
     int[] codon;
     SequenceI ds = seq.getDatasetSequence();
index 2289ac6..bd78827 100755 (executable)
@@ -661,7 +661,7 @@ public class Alignment implements AlignmentI
    * jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SearchResults)
    */
   @Override
-  public int findIndex(SearchResults results)
+  public int findIndex(SearchResultsI results)
   {
     int i = 0;
 
index 1d37fa6..7274e5f 100755 (executable)
@@ -426,7 +426,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param results
    * @return -1 or index of sequence in alignment
    */
-  int findIndex(SearchResults results);
+  int findIndex(SearchResultsI results);
 
   /**
    * append sequences and annotation from another alignment object to this one.
diff --git a/src/jalview/datamodel/SearchResultMatchI.java b/src/jalview/datamodel/SearchResultMatchI.java
new file mode 100644 (file)
index 0000000..732f1dc
--- /dev/null
@@ -0,0 +1,30 @@
+package jalview.datamodel;
+
+/**
+ * An interface that describes one matched region of an alignment, as one
+ * contiguous portion of a single dataset sequence
+ */
+public interface SearchResultMatchI
+{
+  /**
+   * Returns the matched sequence
+   * 
+   * @return
+   */
+  SequenceI getSequence();
+
+  /**
+   * Returns the start position of the match in the sequence (base 1)
+   * 
+   * @return
+   */
+  int getStart();
+
+  /**
+   * Returns the end position of the match in the sequence (base 1)
+   * 
+   * @return
+   */
+  int getEnd();
+
+}
\ No newline at end of file
index b9db461..1bf5475 100755 (executable)
 package jalview.datamodel;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 /**
  * Holds a list of search result matches, where each match is a contiguous
  * stretch of a single sequence.
  * 
- * @author gmcarstairs
+ * @author gmcarstairs amwaterhouse
  *
  */
-public class SearchResults
+public class SearchResults implements SearchResultsI
 {
 
-  private List<Match> matches = new ArrayList<Match>();
+  private List<SearchResultMatchI> matches = new ArrayList<SearchResultMatchI>();
 
   /**
    * One match consists of a sequence reference, start and end positions.
    * Discontiguous ranges in a sequence require two or more Match objects.
    */
-  public class Match
+  public class Match implements SearchResultMatchI
   {
     SequenceI sequence;
 
@@ -55,7 +55,10 @@ public class SearchResults
     int end;
 
     /**
-     * Constructor
+     * create a Match on a range of sequence. Match always holds region in
+     * forwards order, even if given in reverse order (such as from a mapping to
+     * a reverse strand); this avoids trouble for routines that highlight search
+     * results etc
      * 
      * @param seq
      *          a sequence
@@ -80,48 +83,54 @@ public class SearchResults
       }
       else
       {
+        // TODO: JBP could mark match as being specified in reverse direction
+        // for use
+        // by caller ? e.g. visualizing reverse strand highlight
         this.start = end;
         this.end = start;
       }
     }
 
+    /* (non-Javadoc)
+     * @see jalview.datamodel.SearchResultMatchI#getSequence()
+     */
+    @Override
     public SequenceI getSequence()
     {
       return sequence;
     }
 
+    /* (non-Javadoc)
+     * @see jalview.datamodel.SearchResultMatchI#getStart()
+     */
+    @Override
     public int getStart()
     {
       return start;
     }
 
+    /* (non-Javadoc)
+     * @see jalview.datamodel.SearchResultMatchI#getEnd()
+     */
+    @Override
     public int getEnd()
     {
       return end;
     }
 
     /**
-     * Returns the string of characters in the matched region, prefixed by the
-     * start position, e.g. "12CGT" or "208K"
+     * Returns a representation as "seqid/start-end"
      */
     @Override
     public String toString()
     {
-      final int from = Math.max(start - 1, 0);
-      String startPosition = String.valueOf(from);
-      return startPosition + getCharacters();
-    }
-
-    /**
-     * Returns the string of characters in the matched region.
-     */
-    public String getCharacters()
-    {
-      char[] chars = sequence.getSequence();
-      // convert start/end to base 0 (with bounds check)
-      final int from = Math.max(start - 1, 0);
-      final int to = Math.min(end, chars.length + 1);
-      return String.valueOf(Arrays.copyOfRange(chars, from, to));
+      StringBuilder sb = new StringBuilder();
+      if (sequence != null)
+      {
+        sb.append(sequence.getName()).append("/");
+      }
+      sb.append(start).append("-").append(end);
+      return sb.toString();
     }
 
     public void setSequence(SequenceI seq)
@@ -150,46 +159,38 @@ public class SearchResults
     @Override
     public boolean equals(Object obj)
     {
-      if (obj == null || !(obj instanceof Match))
+      if (obj == null || !(obj instanceof SearchResultMatchI))
       {
         return false;
       }
-      Match m = (Match) obj;
-      return (this.sequence == m.sequence && this.start == m.start && this.end == m.end);
+      SearchResultMatchI m = (SearchResultMatchI) obj;
+      return (sequence == m.getSequence() && start == m.getStart() && end == m
+              .getEnd());
     }
   }
 
-  /**
-   * This method replaces the old search results which merely held an alignment
-   * index of search matches. This broke when sequences were moved around the
-   * alignment
-   * 
-   * @param seq
-   *          Sequence
-   * @param start
-   *          int
-   * @param end
-   *          int
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#addResult(jalview.datamodel.SequenceI, int, int)
    */
-  public void addResult(SequenceI seq, int start, int end)
+  @Override
+  public SearchResultMatchI addResult(SequenceI seq, int start, int end)
   {
-    matches.add(new Match(seq, start, end));
+    Match m = new Match(seq, start, end);
+    matches.add(m);
+    return m;
   }
 
-  /**
-   * Quickly check if the given sequence is referred to in the search results
-   * 
-   * @param sequence
-   *          (specific alignment sequence or a dataset sequence)
-   * @return true if the results involve sequence
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#involvesSequence(jalview.datamodel.SequenceI)
    */
+  @Override
   public boolean involvesSequence(SequenceI sequence)
   {
     SequenceI ds = sequence.getDatasetSequence();
-    for (Match m : matches)
+    for (SearchResultMatchI _m : matches)
     {
-      if (m.sequence != null
-              && (m.sequence == sequence || m.sequence == ds))
+      SequenceI matched = _m.getSequence();
+      if (matched != null && (matched == sequence || matched == ds))
       {
         return true;
       }
@@ -197,11 +198,10 @@ public class SearchResults
     return false;
   }
 
-  /**
-   * This Method returns the search matches which lie between the start and end
-   * points of the sequence in question. It is optimised for returning objects
-   * for drawing on SequenceCanvas
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#getResults(jalview.datamodel.SequenceI, int, int)
    */
+  @Override
   public int[] getResults(SequenceI sequence, int start, int end)
   {
     if (matches.isEmpty())
@@ -213,8 +213,11 @@ public class SearchResults
     int[] tmp = null;
     int resultLength, matchStart = 0, matchEnd = 0;
     boolean mfound;
-    for (Match m : matches)
+    Match m;
+    for (SearchResultMatchI _m : matches)
     {
+      m = (Match) _m;
+
       mfound = false;
       if (m.sequence == sequence)
       {
@@ -269,97 +272,76 @@ public class SearchResults
     return result;
   }
 
-  public int getSize()
-  {
-    return matches.size();
-  }
-
-  public SequenceI getResultSequence(int index)
-  {
-    return matches.get(index).sequence;
-  }
-
-  /**
-   * Returns the start position of the i'th match in the search results.
-   * 
-   * @param i
-   * @return
-   */
-  public int getResultStart(int i)
+  @Override
+  public int markColumns(SequenceCollectionI sqcol, BitSet bs)
   {
-    return matches.get(i).start;
+    int count = 0;
+    BitSet mask = new BitSet();
+    for (SequenceI s : sqcol.getSequences())
+    {
+      int[] cols = getResults(s, sqcol.getStartRes(), sqcol.getEndRes());
+      if (cols != null)
+      {
+        for (int pair = 0; pair < cols.length; pair += 2)
+        {
+          mask.set(cols[pair], cols[pair + 1] + 1);
+        }
+      }
+    }
+    // compute columns that were newly selected
+    BitSet original = (BitSet) bs.clone();
+    original.and(mask);
+    count = mask.cardinality() - original.cardinality();
+    // and mark ranges not already marked
+    bs.or(mask);
+    return count;
   }
 
-  /**
-   * Returns the end position of the i'th match in the search results.
-   * 
-   * @param i
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#getSize()
    */
-  public int getResultEnd(int i)
+  @Override
+  public int getSize()
   {
-    return matches.get(i).end;
+    return matches.size();
   }
 
-  /**
-   * Returns true if no search result matches are held.
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#isEmpty()
    */
+  @Override
   public boolean isEmpty()
   {
     return matches.isEmpty();
   }
 
-  /**
-   * Returns the list of matches.
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#getResults()
    */
-  public List<Match> getResults()
+  @Override
+  public List<SearchResultMatchI> getResults()
   {
     return matches;
   }
 
   /**
-   * Return the results as a string of characters (bases) prefixed by start
-   * position(s). Meant for use when the context ensures that all matches are to
-   * regions of the same sequence (otherwise the result is meaningless).
+   * Return the results as a list of matches [seq1/from-to, seq2/from-to, ...]
    * 
    * @return
    */
   @Override
   public String toString()
   {
-    StringBuilder result = new StringBuilder(256);
-    for (Match m : matches)
-    {
-      result.append(m.toString());
-    }
-    return result.toString();
-  }
-
-  /**
-   * Return the results as a string of characters (bases). Meant for use when
-   * the context ensures that all matches are to regions of the same sequence
-   * (otherwise the result is meaningless).
-   * 
-   * @return
-   */
-  public String getCharacters()
-  {
-    StringBuilder result = new StringBuilder(256);
-    for (Match m : matches)
-    {
-      result.append(m.getCharacters());
-    }
-    return result.toString();
+    return matches == null ? "" : matches.toString();
   }
 
   /**
-   * Hashcode is has derived from the list of matches. This ensures that when
-   * two SearchResults objects satisfy the test for equals(), then they have the
+   * Hashcode is derived from the list of matches. This ensures that when two
+   * SearchResults objects satisfy the test for equals(), then they have the
    * same hashcode.
+   * 
+   * @see Match#hashCode()
+   * @see java.util.AbstractList#hashCode()
    */
   @Override
   public int hashCode()
@@ -374,11 +356,11 @@ public class SearchResults
   @Override
   public boolean equals(Object obj)
   {
-    if (obj == null || !(obj instanceof SearchResults))
+    if (obj == null || !(obj instanceof SearchResultsI))
     {
       return false;
     }
-    SearchResults sr = (SearchResults) obj;
-    return ((ArrayList<Match>) this.matches).equals(sr.matches);
+    SearchResultsI sr = (SearchResultsI) obj;
+    return matches.equals(sr.getResults());
   }
 }
diff --git a/src/jalview/datamodel/SearchResultsI.java b/src/jalview/datamodel/SearchResultsI.java
new file mode 100644 (file)
index 0000000..93183f2
--- /dev/null
@@ -0,0 +1,84 @@
+package jalview.datamodel;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * An interface describing the result of a search or other operation which
+ * highlights matched regions of an alignment
+ */
+public interface SearchResultsI
+{
+
+  /**
+   * Adds one region to the results
+   * 
+   * @param seq
+   *          Sequence
+   * @param start
+   *          int
+   * @param end
+   *          int
+   * @return
+   */
+  SearchResultMatchI addResult(SequenceI seq, int start, int end);
+
+  /**
+   * Answers true if the search results include the given sequence (or its
+   * dataset sequence), else false
+   * 
+   * @param sequence
+   * @return
+   */
+  boolean involvesSequence(SequenceI sequence);
+
+  /**
+   * Returns an array of [from, to, from, to..] matched columns (base 0) between
+   * the given start and end columns of the given sequence. Returns null if no
+   * matches overlap the specified region.
+   * <p>
+   * Implementations should provide an optimised method to return locations to
+   * highlight on a visible portion of an alignment.
+   * 
+   * @param sequence
+   * @param start
+   *          first column of range (base 0, inclusive)
+   * @param end
+   *          last column of range base 0, inclusive)
+   * @return int[]
+   */
+  int[] getResults(SequenceI sequence, int start, int end);
+
+  /**
+   * Returns the number of matches found
+   * 
+   * @return
+   */
+  int getSize();
+
+  /**
+   * Returns true if no search result matches are held.
+   * 
+   * @return
+   */
+  boolean isEmpty();
+
+  /**
+   * Returns the list of matches.
+   * 
+   * @return
+   */
+  List<SearchResultMatchI> getResults();
+
+  /**
+   * Set bits in a bitfield for all columns in the given sequence collection
+   * that are highlighted
+   * 
+   * @param sqcol
+   *          the set of sequences to search for highlighted regions
+   * @param bs
+   *          bitset to set
+   * @return number of bits set
+   */
+  int markColumns(SequenceCollectionI sqcol, BitSet bs);
+}
\ No newline at end of file
index 1372749..6a637e3 100644 (file)
@@ -670,6 +670,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           toggleHiddenRegions(toggleSeqs, toggleCols);
           break;
         }
+        case KeyEvent.VK_B:
+        {
+          boolean toggleSel = evt.isControlDown() || evt.isMetaDown();
+          boolean modifyExisting = true; // always modify, don't clear
+                                         // evt.isShiftDown();
+          boolean invertHighlighted = evt.isAltDown();
+          avc.markHighlightedColumns(invertHighlighted, modifyExisting,
+                  toggleSel);
+          break;
+        }
         case KeyEvent.VK_PAGE_UP:
           if (viewport.getWrapAlignment())
           {
@@ -2909,8 +2919,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.setFollowHighlight(state);
     if (state)
     {
-      alignPanel.scrollToPosition(
-              alignPanel.getSeqPanel().seqCanvas.searchResults, false);
+      alignPanel.scrollToPosition(viewport.getSearchResults(), false);
     }
   }
 
@@ -5932,6 +5941,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     return false;
   }
+
+  @Override
+  protected void selectHighlightedColumns_actionPerformed(
+          ActionEvent actionEvent)
+  {
+    // include key modifier check in case user selects from menu
+    avc.markHighlightedColumns(
+            (actionEvent.getModifiers() & ActionEvent.ALT_MASK) != 0,
+            true,
+            (actionEvent.getModifiers() & (ActionEvent.META_MASK | ActionEvent.CTRL_MASK)) != 0);
+  }
 }
 
 class PrintThread extends Thread
index d0a0f11..03aee5d 100644 (file)
@@ -37,6 +37,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -1042,7 +1043,7 @@ public class AlignViewport extends AlignmentViewport implements
      * there is no complement, or it is not following highlights, or no mapping
      * is found, the result will be empty.
      */
-    SearchResults sr = new SearchResults();
+    SearchResultsI sr = new SearchResults();
     int verticalOffset = findComplementScrollTarget(sr);
     if (!sr.isEmpty())
     {
index 921429b..dc1f95b 100644 (file)
@@ -25,7 +25,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -297,7 +297,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Highlight the given results on the alignment.
    * 
    */
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
     scrollToPosition(results);
     getSeqPanel().seqCanvas.highlightSearchResults(results);
@@ -309,7 +309,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * 
    * @param results
    */
-  public boolean scrollToPosition(SearchResults results)
+  public boolean scrollToPosition(SearchResultsI results)
   {
     return scrollToPosition(results, 0, true, false);
   }
@@ -322,7 +322,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param redrawOverview
    * @return
    */
-  public boolean scrollToPosition(SearchResults searchResults,
+  public boolean scrollToPosition(SearchResultsI searchResults,
           boolean redrawOverview)
   {
     return scrollToPosition(searchResults, 0, redrawOverview, false);
@@ -342,7 +342,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          if true, try to centre the search results horizontally in the view
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results,
+  public boolean scrollToPosition(SearchResultsI results,
           int verticalOffset, boolean redrawOverview, boolean centre)
   {
     int startv, endv, starts, ends;
@@ -1825,14 +1825,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param verticalOffset
    *          the number of visible sequences to show above the mapped region
    */
-  public void scrollToCentre(SearchResults sr, int verticalOffset)
+  public void scrollToCentre(SearchResultsI sr, int verticalOffset)
   {
     /*
      * 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);
+    SequenceI mappedTo = sr.getResults().get(0).getSequence();
     List<SequenceI> seqs = av.getAlignment().getSequences();
 
     /*
index 426ea32..83d1612 100644 (file)
@@ -22,6 +22,7 @@ package jalview.gui;
 
 import jalview.api.FeatureColourI;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.FeatureColour;
@@ -200,7 +201,7 @@ public class FeatureRenderer extends
             start.setValue(new Integer(features[index].getBegin()));
             end.setValue(new Integer(features[index].getEnd()));
 
-            SearchResults highlight = new SearchResults();
+            SearchResultsI highlight = new SearchResults();
             highlight.addResult(sequences[0], features[index].getBegin(),
                     features[index].getEnd());
 
index 6bff69a..a8dbc38 100755 (executable)
@@ -20,7 +20,8 @@
  */
 package jalview.gui;
 
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.jbgui.GFinder;
@@ -67,7 +68,7 @@ public class Finder extends GFinder
 
   int resIndex = -1;
 
-  SearchResults searchResults;
+  SearchResultsI searchResults;
 
   /**
    * Creates a new Finder object with no associated viewport or panel.
@@ -109,6 +110,7 @@ public class Finder extends GFinder
             KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
     getRootPane().getActionMap().put("Cancel", new AbstractAction()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         escapeActionPerformed();
@@ -130,6 +132,7 @@ public class Finder extends GFinder
    * 
    * @param e
    */
+  @Override
   public void findNext_actionPerformed(ActionEvent e)
   {
     if (getFocusedViewport())
@@ -143,6 +146,7 @@ public class Finder extends GFinder
    * 
    * @param e
    */
+  @Override
   public void findAll_actionPerformed(ActionEvent e)
   {
     if (getFocusedViewport())
@@ -198,19 +202,22 @@ public class Finder extends GFinder
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void createNewGroup_actionPerformed(ActionEvent e)
   {
     SequenceI[] seqs = new SequenceI[searchResults.getSize()];
     SequenceFeature[] features = new SequenceFeature[searchResults
             .getSize()];
 
-    for (int i = 0; i < searchResults.getSize(); i++)
+    int i = 0;
+    for (SearchResultMatchI match : searchResults.getResults())
     {
-      seqs[i] = searchResults.getResultSequence(i).getDatasetSequence();
+      seqs[i] = match.getSequence().getDatasetSequence();
 
       features[i] = new SequenceFeature(textfield.getText().trim(),
-              "Search Results", null, searchResults.getResultStart(i),
-              searchResults.getResultEnd(i), "Search Results");
+              "Search Results", null, match.getStart(), match.getEnd(),
+              "Search Results");
+      i++;
     }
 
     if (ap.getSeqPanel().seqCanvas.getFeatureRenderer().amendFeatures(seqs,
@@ -256,7 +263,7 @@ public class Finder extends GFinder
 
     searchResults = finder.getSearchResults(); // find(regex,
     // caseSensitive.isSelected(), )
-    Vector idMatch = finder.getIdMatch();
+    Vector<SequenceI> idMatch = finder.getIdMatch();
     boolean haveResults = false;
     // set or reset the GUI
     if ((idMatch.size() > 0))
index 760ece0..d015292 100755 (executable)
@@ -21,7 +21,7 @@
 package jalview.gui;
 
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.ScaleRenderer;
@@ -62,8 +62,6 @@ public class SeqCanvas extends JComponent
 
   AlignViewport av;
 
-  SearchResults searchResults = null;
-
   boolean fastPaint = false;
 
   int LABEL_WEST;
@@ -740,10 +738,10 @@ public class SeqCanvas extends JComponent
 
       // / Highlight search Results once all sequences have been drawn
       // ////////////////////////////////////////////////////////
-      if (searchResults != null)
+      if (av.hasSearchResults())
       {
-        int[] visibleResults = searchResults.getResults(nextSeq, startRes,
-                endRes);
+        int[] visibleResults = av.getSearchResults().getResults(nextSeq,
+                startRes, endRes);
         if (visibleResults != null)
         {
           for (int r = 0; r < visibleResults.length; r += 2)
@@ -965,11 +963,11 @@ public class SeqCanvas extends JComponent
    * @param results
    *          DOCUMENT ME!
    */
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
     img = null;
 
-    searchResults = results;
+    av.setSearchResults(results);
 
     repaint();
   }
index 73ed744..36fb052 100644 (file)
@@ -27,8 +27,9 @@ import jalview.commands.EditCommand.Action;
 import jalview.commands.EditCommand.Edit;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -132,7 +133,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
   StructureSelectionManager ssm;
 
-  SearchResults lastSearchResults;
+  SearchResultsI lastSearchResults;
 
   /**
    * Creates a new SeqPanel object.
@@ -676,7 +677,7 @@ public class SeqPanel extends JPanel implements MouseListener,
    * the start of the highlighted region.
    */
   @Override
-  public void highlightSequence(SearchResults results)
+  public void highlightSequence(SearchResultsI results)
   {
     if (results == null || results.equals(lastSearchResults))
     {
@@ -910,7 +911,7 @@ public class SeqPanel extends JPanel implements MouseListener,
    * 
    * @param results
    */
-  private void setStatusMessage(SearchResults results)
+  private void setStatusMessage(SearchResultsI results)
   {
     AlignmentI al = this.av.getAlignment();
     int sequenceIndex = al.findIndex(results);
@@ -919,7 +920,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       return;
     }
     SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
-    for (Match m : results.getResults())
+    for (SearchResultMatchI m : results.getResults())
     {
       SequenceI seq = m.getSequence();
       if (seq.getDatasetSequence() != null)
@@ -1501,7 +1502,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
       if (features != null && features.size() > 0)
       {
-        SearchResults highlight = new SearchResults();
+        SearchResultsI highlight = new SearchResults();
         highlight.addResult(sequence, features.get(0).getBegin(), features
                 .get(0).getEnd());
         seqCanvas.highlightSearchResults(highlight);
index 1be97f5..fd9c584 100644 (file)
@@ -68,41 +68,25 @@ public class BioJsHTMLOutput extends HTMLOutput
     exportStarted();
     try
     {
-
       if (outputFile == null)
       {
         outputFile = getOutputFile();
       }
       generatedFile = new File(outputFile);
-
-      String bioJSON = getBioJSONData();
-      String bioJSTemplateString = HTMLOutput.readFileAsString(getCurrentBJSTemplateFile());
-      String generatedBioJsWithJalviewAlignmentAsJson = bioJSTemplateString
-              .replaceAll("#sequenceData#", bioJSON).toString();
-
-      PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
-              generatedFile));
-      out.print(generatedBioJsWithJalviewAlignmentAsJson);
-      out.flush();
-      out.close();
-      exportCompleted();
-      setProgressMessage(MessageManager.formatMessage(
-              "status.export_complete", "BioJS"));
-
-    } catch (NoFileSelectedException ex)
-    {
-      // do noting if no file was selected
-    } catch (OutOfMemoryError err)
+    } catch (NoFileSelectedException e)
     {
-      System.out.println("########################\n" + "OUT OF MEMORY "
-              + outputFile + "\n" + "########################");
-      new OOMWarning("Creating Image for " + outputFile, err);
+      setProgressMessage(MessageManager.formatMessage(
+              "status.cancelled_image_export_operation", "BioJS MSA"));
+      return;
     } catch (Exception e)
     {
       setProgressMessage(MessageManager.formatMessage(
-              "info.error_creating_file", "HTML"));
+              "info.error_creating_file", "BioJS MSA"));
       e.printStackTrace();
+      return;
     }
+    new Thread(this).start();
+
   }
 
 
@@ -294,4 +278,38 @@ public class BioJsHTMLOutput extends HTMLOutput
     return generatedFile;
   }
 
+  @Override
+  public void run()
+  {
+    try
+    {
+      String bioJSON = getBioJSONData();
+      String bioJSTemplateString = HTMLOutput
+              .readFileAsString(getCurrentBJSTemplateFile());
+      String generatedBioJsWithJalviewAlignmentAsJson = bioJSTemplateString
+              .replaceAll("#sequenceData#", bioJSON).toString();
+
+      PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
+              generatedFile));
+      out.print(generatedBioJsWithJalviewAlignmentAsJson);
+      out.flush();
+      out.close();
+      setProgressMessage(MessageManager.formatMessage(
+              "status.export_complete", "BioJS"));
+      exportCompleted();
+
+    } catch (OutOfMemoryError err)
+    {
+      System.out.println("########################\n" + "OUT OF MEMORY "
+              + generatedFile + "\n" + "########################");
+      new OOMWarning("Creating Image for " + generatedFile, err);
+    } catch (Exception e)
+    {
+      setProgressMessage(MessageManager.formatMessage(
+              "info.error_creating_file", "HTML"));
+      e.printStackTrace();
+    }
+
+  }
+
 }
index a422a38..d58bd67 100755 (executable)
@@ -35,7 +35,7 @@ import java.net.URL;
 import java.util.Objects;
 
 
-public abstract class HTMLOutput
+public abstract class HTMLOutput implements Runnable
 {
   protected AlignmentPanel ap;
 
@@ -54,52 +54,58 @@ public abstract class HTMLOutput
     }
   }
 
-
   public String getBioJSONData()
   {
+    return getBioJSONData(null);
+  }
+
+  public String getBioJSONData(AlignExportSettingI exportSettings)
+  {
     if (!isEmbedData())
     {
       return null;
     }
-    AlignExportSettingI exportSettings = new AlignExportSettingI()
+    if (exportSettings == null)
     {
-      @Override
-      public boolean isExportHiddenSequences()
+      exportSettings = new AlignExportSettingI()
       {
-        return true;
-      }
-
-      @Override
-      public boolean isExportHiddenColumns()
-      {
-        return true;
-      }
+        @Override
+        public boolean isExportHiddenSequences()
+        {
+          return true;
+        }
 
-      @Override
-      public boolean isExportAnnotations()
-      {
-        return true;
-      }
+        @Override
+        public boolean isExportHiddenColumns()
+        {
+          return true;
+        }
 
-      @Override
-      public boolean isExportFeatures()
-      {
-        return true;
-      }
+        @Override
+        public boolean isExportAnnotations()
+        {
+          return true;
+        }
 
-      @Override
-      public boolean isExportGroups()
-      {
-        return true;
-      }
+        @Override
+        public boolean isExportFeatures()
+        {
+          return true;
+        }
 
-      @Override
-      public boolean isCancelled()
-      {
-        return false;
-      }
+        @Override
+        public boolean isExportGroups()
+        {
+          return true;
+        }
 
-    };
+        @Override
+        public boolean isCancelled()
+        {
+          return false;
+        }
+      };
+    }
     AlignmentExportData exportData = jalview.gui.AlignFrame
             .getAlignmentForExport(JSONFile.FILE_DESC,
                     ap.getAlignViewport(), exportSettings);
@@ -264,7 +270,7 @@ public abstract class HTMLOutput
     jvFileChooser.setFileView(new JalviewFileView());
 
     jvFileChooser.setDialogTitle(MessageManager
-            .getString("label.save_as_biojs_html"));
+            .getString("label.save_as_html"));
     jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
 
     int fileChooserOpt = jvFileChooser.showSaveDialog(null);
@@ -276,9 +282,6 @@ public abstract class HTMLOutput
     }
     else
     {
-      pIndicator.setProgressBar(MessageManager.formatMessage(
-              "status.cancelled_image_export_operation", "BioJS"),
-              pSessionId);
       throw new NoFileSelectedException("No file was selected.");
     }
     return selectedFile;
index b01f11f..1ec3a4e 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.io;
 
+import jalview.exceptions.NoFileSelectedException;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.HTMLOptions;
 import jalview.gui.OOMWarning;
@@ -38,7 +39,6 @@ import org.jfree.graphics2d.svg.SVGHints;
 public class HtmlSvgOutput extends HTMLOutput
 {
 
-  private File generatedFile;
 
   public HtmlSvgOutput(AlignmentPanel ap)
   {
@@ -46,16 +46,21 @@ public class HtmlSvgOutput extends HTMLOutput
   }
 
   @Override
-  public void exportHTML(String file)
+  public void exportHTML(String outputFile)
   {
     exportStarted();
     try
     {
-      if (file == null)
+      if (outputFile == null)
       {
-        file = getOutputFile();
+        outputFile = getOutputFile();
       }
-      generatedFile = new File(file);
+      generatedFile = new File(outputFile);
+    } catch (NoFileSelectedException e)
+    {
+      setProgressMessage(MessageManager.formatMessage(
+              "status.cancelled_image_export_operation", "HTML"));
+      return;
     } catch (Exception e)
     {
       setProgressMessage(MessageManager.formatMessage(
@@ -63,89 +68,7 @@ public class HtmlSvgOutput extends HTMLOutput
       e.printStackTrace();
       return;
     }
-    new Thread()
-    {
-      @Override
-      public void run()
-      {
-        try
-        {
-          setProgressMessage(null);
-          setProgressMessage(MessageManager.formatMessage(
-                  "status.exporting_alignment_as_x_file", "HTML"));
-          AlignmentDimension aDimension = ap.getAlignmentDimension();
-          SVGGraphics2D idPanelGraphics = new SVGGraphics2D(
-                  aDimension.getWidth(),
-                  aDimension.getHeight());
-          SVGGraphics2D alignPanelGraphics = new SVGGraphics2D(
-                  aDimension.getWidth(),
-                  aDimension.getHeight());
-
-          String renderStyle = jalview.bin.Cache.getDefault(
-                  "HTML_RENDERING", "Prompt each time");
-
-          // If we need to prompt, and if the GUI is visible then
-          // Prompt for rendering style
-          if (renderStyle.equalsIgnoreCase("Prompt each time")
-                  && !isHeadless())
-          {
-            HTMLOptions svgOption = new HTMLOptions();
-            renderStyle = svgOption.getValue();
-
-            if (renderStyle == null || svgOption.cancelled)
-            {
-              setProgressMessage(MessageManager.formatMessage(
-                      "status.cancelled_image_export_operation", "HTML"));
-              return;
-            }
-          }
-
-          if (renderStyle.equalsIgnoreCase("Lineart"))
-          {
-            idPanelGraphics.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
-                    SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
-            alignPanelGraphics.setRenderingHint(
-                    SVGHints.KEY_DRAW_STRING_TYPE,
-                    SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
-          }
-          if (ap.av.getWrapAlignment())
-          {
-            printWrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
-                    alignPanelGraphics);
-          }
-          else
-          {
-          printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
- idPanelGraphics, alignPanelGraphics);
-          }
-
-          String idPanelSvgData = idPanelGraphics.getSVGDocument();
-          String alignPanelSvgData = alignPanelGraphics.getSVGDocument();
-          String jsonData = getBioJSONData();
-          String htmlData = getHtml(idPanelSvgData, alignPanelSvgData, jsonData,
- ap.av.getWrapAlignment());
-          FileOutputStream out = new FileOutputStream(generatedFile);
-          out.write(htmlData.getBytes());
-          out.flush();
-          out.close();
-          exportCompleted();
-        } catch (OutOfMemoryError err)
-        {
-          System.out.println("########################\n"
-                  + "OUT OF MEMORY " + generatedFile + "\n"
-                  + "########################");
-          new OOMWarning("Creating Image for " + generatedFile, err);
-        } catch (Exception e)
-        {
-          e.printStackTrace();
-          setProgressMessage(MessageManager.formatMessage(
-                  "info.error_creating_file", "HTML"));
-        }
-        setProgressMessage(MessageManager.formatMessage(
-                "status.export_complete", "HTML"));
-      }
-    }.start();
-
+    new Thread(this).start();
   }
 
 
@@ -243,11 +166,9 @@ public class HtmlSvgOutput extends HTMLOutput
               .append(alignmentSvg).append("</div>");
       htmlSvg.append("<script language=\"JavaScript\" type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js\"></script>\n"
               + "<script language=\"JavaScript\" type=\"text/javascript\"  src=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js\"></script>\n");
-
     }
 
     // javascript for launching file in Jalview
-
     htmlSvg.append("<script language=\"JavaScript\">\n");
     htmlSvg.append("function openJalviewUsingCurrentUrl(){\n");
     htmlSvg.append("    var json = JSON.parse(document.getElementById(\"seqData\").innerHTML);\n");
@@ -272,9 +193,9 @@ public class HtmlSvgOutput extends HTMLOutput
     htmlSvg.append("    document.body.removeChild(myForm);\n");
     htmlSvg.append("}\n");
 
-    // jquery facebox for displaying raw BioJSON data");
     if (jsonData != null)
     {
+      // JQuery FaceBox for displaying raw BioJSON data");
       File faceBoxJsFile = new File("examples/javascript/facebox-1.3.js");
       try
       {
@@ -308,4 +229,79 @@ public class HtmlSvgOutput extends HTMLOutput
   {
     return generatedFile;
   }
+
+  @Override
+  public void run()
+  {
+    try
+    {
+      setProgressMessage(null);
+      setProgressMessage(MessageManager.formatMessage(
+              "status.exporting_alignment_as_x_file", "HTML"));
+      AlignmentDimension aDimension = ap.getAlignmentDimension();
+      SVGGraphics2D idPanelGraphics = new SVGGraphics2D(
+              aDimension.getWidth(), aDimension.getHeight());
+      SVGGraphics2D alignPanelGraphics = new SVGGraphics2D(
+              aDimension.getWidth(), aDimension.getHeight());
+
+      String renderStyle = jalview.bin.Cache.getDefault("HTML_RENDERING",
+              "Prompt each time");
+
+      // If we need to prompt, and if the GUI is visible then
+      // Prompt for rendering style
+      if (renderStyle.equalsIgnoreCase("Prompt each time") && !isHeadless())
+      {
+        HTMLOptions svgOption = new HTMLOptions();
+        renderStyle = svgOption.getValue();
+
+        if (renderStyle == null || svgOption.cancelled)
+        {
+          setProgressMessage(MessageManager.formatMessage(
+                  "status.cancelled_image_export_operation", "HTML"));
+          return;
+        }
+      }
+
+      if (renderStyle.equalsIgnoreCase("Lineart"))
+      {
+        idPanelGraphics.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
+                SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
+        alignPanelGraphics.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
+                SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
+      }
+      if (ap.av.getWrapAlignment())
+      {
+        printWrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
+                alignPanelGraphics);
+      }
+      else
+      {
+        printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
+                idPanelGraphics, alignPanelGraphics);
+      }
+
+      String idPanelSvgData = idPanelGraphics.getSVGDocument();
+      String alignPanelSvgData = alignPanelGraphics.getSVGDocument();
+      String jsonData = getBioJSONData();
+      String htmlData = getHtml(idPanelSvgData, alignPanelSvgData,
+              jsonData, ap.av.getWrapAlignment());
+      FileOutputStream out = new FileOutputStream(generatedFile);
+      out.write(htmlData.getBytes());
+      out.flush();
+      out.close();
+      setProgressMessage(MessageManager.formatMessage(
+              "status.export_complete", "HTML"));
+      exportCompleted();
+    } catch (OutOfMemoryError err)
+    {
+      System.out.println("########################\n" + "OUT OF MEMORY "
+              + generatedFile + "\n" + "########################");
+      new OOMWarning("Creating Image for " + generatedFile, err);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      setProgressMessage(MessageManager.formatMessage(
+              "info.error_creating_file", "HTML"));
+    }
+  }
 }
index 6b94559..af8a172 100755 (executable)
@@ -2181,6 +2181,19 @@ public class GAlignFrame extends JInternalFrame
         alignmentProperties();
       }
     });
+    JMenuItem selectHighlighted = new JMenuItem(
+            MessageManager.getString("action.select_highlighted_columns"));
+    selectHighlighted.setToolTipText(MessageManager
+            .getString("tooltip.select_highlighted_columns"));
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent actionEvent)
+      {
+        selectHighlightedColumns_actionPerformed(actionEvent);
+      }
+    };
+    selectHighlighted.addActionListener(al);
     JMenu tooltipSettingsMenu = new JMenu(
             MessageManager.getString("label.sequence_id_tooltip"));
     JMenu autoAnnMenu = new JMenu(
@@ -2382,12 +2395,20 @@ public class GAlignFrame extends JInternalFrame
     selectMenu.add(grpsFromSelection);
     selectMenu.add(deleteGroups);
     selectMenu.add(annotationColumn);
+    selectMenu.add(selectHighlighted);
     // TODO - determine if the listenToViewSelections button is needed : see bug
     // JAL-574
     // selectMenu.addSeparator();
     // selectMenu.add(listenToViewSelections);
   }
 
+  protected void selectHighlightedColumns_actionPerformed(
+          ActionEvent actionEvent)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
   /**
    * Generate the reverse sequence (or reverse complement if the flag is true)
    * and add it to the alignment
index 771b8a0..81ff739 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.structure;
 
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
 
 public interface SequenceListener
@@ -27,7 +28,7 @@ public interface SequenceListener
   // TODO remove this? never called on SequenceListener type
   public void mouseOverSequence(SequenceI sequence, int index, int pos);
 
-  public void highlightSequence(jalview.datamodel.SearchResults results);
+  public void highlightSequence(SearchResultsI results);
 
   // TODO remove this? never called
   public void updateColours(SequenceI sequence, int index);
index cad2303..b3968a3 100644 (file)
@@ -31,6 +31,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
 import jalview.ext.jmol.JmolParser;
 import jalview.gui.IProgressIndicator;
@@ -805,7 +806,7 @@ public class StructureSelectionManager
       return;
     }
 
-    SearchResults results = new SearchResults();
+    SearchResultsI results = new SearchResults();
     for (AtomSpec atom : atoms)
     {
       SequenceI lastseq = null;
@@ -855,7 +856,7 @@ public class StructureSelectionManager
   {
     boolean hasSequenceListeners = handlingVamsasMo
             || !seqmappings.isEmpty();
-    SearchResults results = null;
+    SearchResultsI results = null;
     if (seqPos == -1)
     {
       seqPos = seq.findPosition(indexpos);
index 1fe452d..f35339c 100644 (file)
@@ -31,8 +31,9 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -194,7 +195,7 @@ public final class MappingUtils
       /*
        * Determine all mappings from this position to mapped sequences.
        */
-      SearchResults sr = buildSearchResults(seq, seqpos, mappings);
+      SearchResultsI sr = buildSearchResults(seq, seqpos, mappings);
 
       if (!sr.isEmpty())
       {
@@ -266,10 +267,10 @@ public final class MappingUtils
    * @param seqmappings
    * @return
    */
-  public static SearchResults buildSearchResults(SequenceI seq, int index,
+  public static SearchResultsI buildSearchResults(SequenceI seq, int index,
           List<AlignedCodonFrame> seqmappings)
   {
-    SearchResults results = new SearchResults();
+    SearchResultsI results = new SearchResults();
     addSearchResults(results, seq, index, seqmappings);
     return results;
   }
@@ -283,7 +284,7 @@ public final class MappingUtils
    * @param index
    * @param seqmappings
    */
-  public static void addSearchResults(SearchResults results, SequenceI seq,
+  public static void addSearchResults(SearchResultsI results, SequenceI seq,
           int index, List<AlignedCodonFrame> seqmappings)
   {
     if (index >= seq.getStart() && index <= seq.getEnd())
@@ -376,15 +377,15 @@ public final class MappingUtils
                */
               List<AlignedCodonFrame> mapping = Arrays
                       .asList(new AlignedCodonFrame[] { acf });
-              SearchResults sr = buildSearchResults(selected,
+              SearchResultsI sr = buildSearchResults(selected,
                       startResiduePos, mapping);
-              for (Match m : sr.getResults())
+              for (SearchResultMatchI m : sr.getResults())
               {
                 mappedStartResidue = m.getStart();
                 mappedEndResidue = m.getEnd();
               }
               sr = buildSearchResults(selected, endResiduePos, mapping);
-              for (Match m : sr.getResults())
+              for (SearchResultMatchI m : sr.getResults())
               {
                 mappedStartResidue = Math.min(mappedStartResidue,
                         m.getStart());
@@ -647,8 +648,8 @@ public final class MappingUtils
        * Get the residue position and find the mapped position.
        */
       int residuePos = fromSeq.findPosition(col);
-      SearchResults sr = buildSearchResults(fromSeq, residuePos, mappings);
-      for (Match m : sr.getResults())
+      SearchResultsI sr = buildSearchResults(fromSeq, residuePos, mappings);
+      for (SearchResultMatchI m : sr.getResults())
       {
         int mappedStartResidue = m.getStart();
         int mappedEndResidue = m.getEnd();
index 438eaf8..fecccb0 100644 (file)
@@ -37,7 +37,7 @@ import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.ProfilesI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
@@ -2718,7 +2718,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    *          the SearchResults to add to
    * @return the offset (below top of visible region) of the matched sequence
    */
-  protected int findComplementScrollTarget(SearchResults sr)
+  protected int findComplementScrollTarget(SearchResultsI sr)
   {
     final AlignViewportI complement = getCodingComplement();
     if (complement == null || !complement.isFollowHighlight())
@@ -2825,6 +2825,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   private boolean selectionIsDefinedGroup = false;
 
+
   @Override
   public boolean isSelectionDefinedGroup()
   {
@@ -2848,4 +2849,27 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return selectionGroup.getContext() == alignment
             || selectionIsDefinedGroup;
   }
+
+  /**
+   * null, or currently highlighted results on this view
+   */
+  private SearchResultsI searchResults = null;
+
+  @Override
+  public boolean hasSearchResults()
+  {
+    return searchResults != null;
+  }
+
+  @Override
+  public void setSearchResults(SearchResultsI results)
+  {
+    searchResults = results;
+  }
+
+  @Override
+  public SearchResultsI getSearchResults()
+  {
+    return searchResults;
+  }
 }
index 4aed7e7..449514a 100644 (file)
@@ -35,8 +35,8 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.Mapping;
-import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
@@ -1119,9 +1119,9 @@ public class AlignmentUtilsTests
     assertEquals(1, mappings.size());
 
     // map G to GGG
-    SearchResults sr = MappingUtils.buildSearchResults(pep1, 1, mappings);
+    SearchResultsI sr = MappingUtils.buildSearchResults(pep1, 1, mappings);
     assertEquals(1, sr.getResults().size());
-    Match m = sr.getResults().get(0);
+    SearchResultMatchI m = sr.getResults().get(0);
     assertSame(cds1Dss, m.getSequence());
     assertEquals(1, m.getStart());
     assertEquals(3, m.getEnd());
@@ -1650,10 +1650,10 @@ public class AlignmentUtilsTests
     List<AlignedCodonFrame> pep1CdsMappings = MappingUtils
             .findMappingsForSequence(cds.getSequenceAt(0), pep1Mappings);
     assertEquals(1, pep1CdsMappings.size());
-    SearchResults sr = MappingUtils.buildSearchResults(pep1, 1,
+    SearchResultsI sr = MappingUtils.buildSearchResults(pep1, 1,
             pep1CdsMappings);
     assertEquals(1, sr.getResults().size());
-    Match m = sr.getResults().get(0);
+    SearchResultMatchI m = sr.getResults().get(0);
     assertEquals(cds.getSequenceAt(0).getDatasetSequence(), m.getSequence());
     assertEquals(1, m.getStart());
     assertEquals(3, m.getEnd());
@@ -2388,9 +2388,9 @@ public class AlignmentUtilsTests
     assertEquals(1, mappings.size());
 
     // map G to GGG
-    SearchResults sr = MappingUtils.buildSearchResults(pep3, 1, mappings);
+    SearchResultsI sr = MappingUtils.buildSearchResults(pep3, 1, mappings);
     assertEquals(1, sr.getResults().size());
-    Match m = sr.getResults().get(0);
+    SearchResultMatchI m = sr.getResults().get(0);
     assertSame(cds.getSequenceAt(0).getDatasetSequence(), m.getSequence());
     assertEquals(1, m.getStart());
     assertEquals(3, m.getEnd());
diff --git a/test/jalview/analysis/FinderTest.java b/test/jalview/analysis/FinderTest.java
new file mode 100644 (file)
index 0000000..971a8c6
--- /dev/null
@@ -0,0 +1,289 @@
+package jalview.analysis;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.Sequence;
+import jalview.gui.AlignFrame;
+import jalview.io.FileLoader;
+import jalview.io.FormatAdapter;
+
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class FinderTest
+{
+  private AlignFrame af;
+
+  private AlignmentI al;
+
+  @BeforeClass(groups = "Functional")
+  public void setUp()
+  {
+    String seqData = "seq1 ABCD--EF-GHI\n" + "seq2 A--BCDefHI\n"
+            + "seq3 --bcdEFH\n" + "seq4 aa---aMMMMMaaa\n";
+    af = new FileLoader().LoadFileWaitTillLoaded(seqData,
+            FormatAdapter.PASTE);
+    al = af.getViewport().getAlignment();
+  }
+
+  /**
+   * Test for find all matches of a regular expression
+   */
+  @Test(groups = "Functional")
+  public void testFindAll_regex()
+  {
+    Finder f = new Finder(al, null);
+    f.setFindAll(true);
+    f.find("E.H"); // 'E, any character, H'
+
+    // should match seq2 efH and seq3 EFH
+    SearchResultsI sr = f.getSearchResults();
+    assertEquals(sr.getSize(), 2);
+    List<SearchResultMatchI> matches = sr.getResults();
+    assertSame(al.getSequenceAt(1), matches.get(0).getSequence());
+    assertSame(al.getSequenceAt(2), matches.get(1).getSequence());
+    assertEquals(matches.get(0).getStart(), 5);
+    assertEquals(matches.get(0).getEnd(), 7);
+    assertEquals(matches.get(1).getStart(), 4);
+    assertEquals(matches.get(1).getEnd(), 6);
+  }
+
+  /**
+   * Test for (undocumented) find residue by position
+   */
+  @Test(groups = "Functional")
+  public void testFind_residueNumber()
+  {
+    Finder f = new Finder(al, null);
+    f.setFindAll(true);
+    f.find("9");
+
+    // seq1 and seq4 have 9 residues; no match in other sequences
+    SearchResultsI sr = f.getSearchResults();
+    assertEquals(sr.getSize(), 2);
+    List<SearchResultMatchI> matches = sr.getResults();
+    assertSame(al.getSequenceAt(0), matches.get(0).getSequence());
+    assertSame(al.getSequenceAt(3), matches.get(1).getSequence());
+    assertEquals(matches.get(0).getStart(), 9);
+    assertEquals(matches.get(0).getEnd(), 9);
+    assertEquals(matches.get(1).getStart(), 9);
+    assertEquals(matches.get(1).getEnd(), 9);
+  }
+
+  /**
+   * Test for find next action
+   */
+  @Test(groups = "Functional")
+  public void testFindNext()
+  {
+    /*
+     * start at second sequence; resIndex of -1
+     * means sequence id / description is searched
+     */
+    Finder f = new Finder(al, null, 1, -1);
+    f.find("e"); // matches id
+
+    assertTrue(f.getSearchResults().isEmpty());
+    assertEquals(f.getIdMatch().size(), 1);
+    assertSame(f.getIdMatch().get(0), al.getSequenceAt(1));
+
+    // resIndex is now 0 - for use in next find next
+    assertEquals(f.getResIndex(), 0);
+    f = new Finder(al, null, 1, 0);
+    f.find("e"); // matches in sequence
+    assertTrue(f.getIdMatch().isEmpty());
+    assertEquals(f.getSearchResults().getSize(), 1);
+    List<SearchResultMatchI> matches = f.getSearchResults().getResults();
+    assertEquals(matches.get(0).getStart(), 5);
+    assertEquals(matches.get(0).getEnd(), 5);
+    assertSame(matches.get(0).getSequence(), al.getSequenceAt(1));
+    // still in the second sequence
+    assertEquals(f.getSeqIndex(), 1);
+    // next residue position to search from is 5
+    // (used as base 0 by RegEx so the same as 6 if base 1)
+    assertEquals(f.getResIndex(), 5);
+
+    // find next from end of sequence - finds next sequence id
+    f = new Finder(al, null, 1, 5);
+    f.find("e");
+    assertEquals(f.getIdMatch().size(), 1);
+    assertSame(f.getIdMatch().get(0), al.getSequenceAt(2));
+  }
+
+  /**
+   * Test for matching within sequence descriptions
+   */
+  @Test(groups = "Functional")
+  public void testFindAll_inDescription()
+  {
+    AlignmentI al2 = new Alignment(al);
+    al2.getSequenceAt(0).setDescription("BRAF");
+    al2.getSequenceAt(1).setDescription("braf");
+    Finder f = new Finder(al2, null);
+    f.setFindAll(true);
+    f.setIncludeDescription(true);
+
+    f.find("rAF");
+    assertEquals(f.getIdMatch().size(), 2);
+    assertSame(f.getIdMatch().get(0), al2.getSequenceAt(0));
+    assertSame(f.getIdMatch().get(1), al2.getSequenceAt(1));
+    assertTrue(f.getSearchResults().isEmpty());
+
+    /*
+     * case sensitive
+     */
+    f = new Finder(al2, null);
+    f.setFindAll(true);
+    f.setCaseSensitive(true);
+    f.setIncludeDescription(true);
+
+    f.find("RAF");
+    assertEquals(f.getIdMatch().size(), 1);
+    assertSame(f.getIdMatch().get(0), al2.getSequenceAt(0));
+    assertTrue(f.getSearchResults().isEmpty());
+
+    /*
+     * match sequence id, description and sequence!
+     */
+    al2.getSequenceAt(0).setDescription("the efh sequence");
+    al2.getSequenceAt(0).setName("mouseEFHkinase");
+    al2.getSequenceAt(1).setName("humanEFHkinase");
+    f = new Finder(al2, null);
+    f.setFindAll(true);
+    f.setIncludeDescription(true);
+
+    /*
+     * sequence matches should have no duplicates
+     */
+    f.find("EFH");
+    assertEquals(f.getIdMatch().size(), 2);
+    assertSame(f.getIdMatch().get(0), al2.getSequenceAt(0));
+    assertSame(f.getIdMatch().get(1), al2.getSequenceAt(1));
+
+    assertEquals(f.getSearchResults().getSize(), 2);
+    SearchResultMatchI match = f.getSearchResults().getResults().get(0);
+    assertSame(al2.getSequenceAt(1), match.getSequence());
+    assertEquals(5, match.getStart());
+    assertEquals(7, match.getEnd());
+    match = f.getSearchResults().getResults().get(1);
+    assertSame(al2.getSequenceAt(2), match.getSequence());
+    assertEquals(4, match.getStart());
+    assertEquals(6, match.getEnd());
+  }
+
+  /**
+   * Test for matching within sequence ids
+   */
+  @Test(groups = "Functional")
+  public void testFindAll_sequenceIds()
+  {
+    Finder f = new Finder(al, null);
+    f.setFindAll(true);
+
+    /*
+     * case insensitive
+     */
+    f.find("SEQ1");
+    assertEquals(f.getIdMatch().size(), 1);
+    assertSame(f.getIdMatch().get(0), al.getSequenceAt(0));
+    assertTrue(f.getSearchResults().isEmpty());
+
+    /*
+     * case sensitive
+     */
+    f = new Finder(al, null);
+    f.setFindAll(true);
+    f.setCaseSensitive(true);
+    f.find("SEQ1");
+    assertTrue(f.getSearchResults().isEmpty());
+
+    /*
+     * match both sequence id and sequence
+     */
+    AlignmentI al2 = new Alignment(al);
+    al2.addSequence(new Sequence("aBz", "xyzabZpqrAbZ"));
+    f = new Finder(al2, null);
+    f.setFindAll(true);
+    f.find("ABZ");
+    assertEquals(f.getIdMatch().size(), 1);
+    assertSame(f.getIdMatch().get(0), al2.getSequenceAt(4));
+    assertEquals(f.getSearchResults().getSize(), 2);
+    SearchResultMatchI match = f.getSearchResults().getResults().get(0);
+    assertSame(al2.getSequenceAt(4), match.getSequence());
+    assertEquals(4, match.getStart());
+    assertEquals(6, match.getEnd());
+    match = f.getSearchResults().getResults().get(1);
+    assertSame(al2.getSequenceAt(4), match.getSequence());
+    assertEquals(10, match.getStart());
+    assertEquals(12, match.getEnd());
+  }
+
+  /**
+   * Test finding all matches of a sequence pattern in an alignment
+   */
+  @Test(groups = "Functional")
+  public void testFindAll_simpleMatch()
+  {
+    Finder f = new Finder(al, null);
+    f.setFindAll(true);
+
+    /*
+     * case insensitive first
+     */
+    f.find("EfH");
+    SearchResultsI searchResults = f.getSearchResults();
+    assertEquals(searchResults.getSize(), 2);
+    SearchResultMatchI match = searchResults.getResults().get(0);
+    assertSame(al.getSequenceAt(1), match.getSequence());
+    assertEquals(5, match.getStart());
+    assertEquals(7, match.getEnd());
+    match = searchResults.getResults().get(1);
+    assertSame(al.getSequenceAt(2), match.getSequence());
+    assertEquals(4, match.getStart());
+    assertEquals(6, match.getEnd());
+
+    /*
+     * case sensitive
+     */
+    f = new Finder(al, null);
+    f.setFindAll(true);
+    f.setCaseSensitive(true);
+    f.find("BC");
+    searchResults = f.getSearchResults();
+    assertEquals(searchResults.getSize(), 2);
+    match = searchResults.getResults().get(0);
+    assertSame(al.getSequenceAt(0), match.getSequence());
+    assertEquals(2, match.getStart());
+    assertEquals(3, match.getEnd());
+    match = searchResults.getResults().get(1);
+    assertSame(al.getSequenceAt(1), match.getSequence());
+    assertEquals(2, match.getStart());
+    assertEquals(3, match.getEnd());
+  }
+
+  /**
+   * Test for JAL-2302 to verify that sub-matches are not included in a find all
+   * result
+   */
+  @Test(groups = "Functional")
+  public void testFind_maximalResultOnly()
+  {
+    Finder f = new Finder(al, null);
+    f.setFindAll(true);
+    f.find("M+");
+    SearchResultsI searchResults = f.getSearchResults();
+    assertEquals(searchResults.getSize(), 1);
+    SearchResultMatchI match = searchResults.getResults().get(0);
+    assertSame(al.getSequenceAt(3), match.getSequence());
+    assertEquals(4, match.getStart()); // dataset sequence positions
+    assertEquals(8, match.getEnd()); // base 1
+  }
+}
index 7fd8965..53eb171 100644 (file)
@@ -23,11 +23,19 @@ package jalview.controller;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.analysis.Finder;
+import jalview.api.AlignViewControllerI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.io.FileLoader;
+import jalview.io.FormatAdapter;
 
+import java.util.Arrays;
 import java.util.BitSet;
 
 import org.testng.annotations.Test;
@@ -116,4 +124,53 @@ public class AlignViewControllerTest
     assertEquals(0, seqCount);
     assertEquals(0, bs.cardinality());
   }
+
+  /**
+   * shameless copy of test data from findFeature for testing mark columns from
+   * highlight
+   */
+  @Test(groups = "Functional")
+  public void testSelectColumnsWithHighlight()
+  {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "seq1 aMMMaaaaaaaaaaaaaaaa\n" + "seq2 aaaMMMMMMMaaaaaaaaaa\n"
+                    + "seq3 aaaaaaaaaaMMMMMaaaaa\n"
+                    + "seq4 aaaaaaaaaaaaaaaaaaaa\n", FormatAdapter.PASTE);
+
+    SearchResultsI sr = new SearchResults();
+    SequenceI[] sqs = af.getViewport().getAlignment().getSequencesArray();
+    SequenceI seq1 = sqs[0];
+    SequenceI seq2 = sqs[1];
+    SequenceI seq3 = sqs[2];
+    SequenceI seq4 = sqs[3];
+
+    /*
+     * features start/end are base 1
+     */
+    sr.addResult(seq1, 2, 4);
+    sr.addResult(seq2, 4, 10);
+    sr.addResult(seq3, 11, 15);
+
+    /*
+     *  test Match/Find works first
+     */
+    Finder f = new Finder(af.getViewport().getAlignment(), null);
+    f.setFindAll(true);
+    f.setCaseSensitive(true);
+    f.find("M+");
+    assertEquals(
+            "Finder found different set of results to manually created SearchResults",
+            sr, f.getSearchResults());
+
+    /*
+     * now check simple mark columns from find operation
+     */
+    af.getViewport().setSearchResults(sr);
+    AlignViewControllerI avc = af.avc;
+
+    avc.markHighlightedColumns(false, false, false);
+    assertTrue("Didn't select highlighted columns", Arrays.deepEquals(af
+            .getViewport().getColumnSelection().getSelectedRanges()
+            .toArray(), new int[][] { { 1, 14 } }));
+  }
 }
index 6f3c7a9..95764d0 100644 (file)
@@ -24,8 +24,6 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.datamodel.SearchResults.Match;
-
 import org.testng.annotations.Test;
 
 public class MatchTest
@@ -34,17 +32,9 @@ public class MatchTest
   @Test(groups = { "Functional" })
   public void testToString()
   {
-    SequenceI seq = new Sequence("", "abcdefghijklm");
-    Match m = new SearchResults().new Match(seq, 3, 5);
-    assertEquals("2cde", m.toString());
-  }
-
-  @Test(groups = { "Functional" })
-  public void testGetCharacters()
-  {
-    SequenceI seq = new Sequence("", "abcdefghijklm");
-    Match m = new SearchResults().new Match(seq, 3, 5);
-    assertEquals("cde", m.getCharacters());
+    SequenceI seq = new Sequence("Seq1", "abcdefghijklm");
+    SearchResultMatchI m = new SearchResults().new Match(seq, 3, 5);
+    assertEquals("Seq1/3-5", m.toString());
   }
 
   @Test(groups = { "Functional" })
@@ -52,8 +42,8 @@ public class MatchTest
   {
     SequenceI seq1 = new Sequence("", "abcdefghijklm");
     SequenceI seq2 = new Sequence("", "abcdefghijklm");
-    SearchResults sr1 = new SearchResults();
-    SearchResults sr2 = new SearchResults();
+    SearchResultsI sr1 = new SearchResults();
+    SearchResultsI sr2 = new SearchResults();
 
     assertFalse(sr1.equals(null));
     assertFalse(sr1.equals(seq1));
@@ -72,7 +62,7 @@ public class MatchTest
     /*
      * same match but on different sequences - not equal
      */
-    SearchResults sr3 = new SearchResults();
+    SearchResultsI sr3 = new SearchResults();
     sr3.addResult(seq2, 1, 1);
     assertFalse(sr1.equals(sr3));
     assertFalse(sr3.equals(sr1));
index f9a0a4f..19e89d2 100644 (file)
@@ -25,8 +25,9 @@ import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.datamodel.SearchResults.Match;
+import java.util.BitSet;
 
+import org.junit.Assert;
 import org.testng.annotations.Test;
 
 public class SearchResultsTest
@@ -35,39 +36,24 @@ public class SearchResultsTest
   @Test(groups = { "Functional" })
   public void testToString()
   {
-    SequenceI seq = new Sequence("", "abcdefghijklm");
-    SearchResults sr = new SearchResults();
+    SequenceI seq = new Sequence("Seq1", "abcdefghijklm");
+    SearchResultsI sr = new SearchResults();
     sr.addResult(seq, 1, 1);
-    assertEquals("0a", sr.toString());
+    assertEquals("[Seq1/1-1]", sr.toString());
     sr.addResult(seq, 3, 5);
-    assertEquals("0a2cde", sr.toString());
+    assertEquals("[Seq1/1-1, Seq1/3-5]", sr.toString());
 
-    seq = new Sequence("", "pqrstuvwxy");
+    seq = new Sequence("Seq2", "pqrstuvwxy");
     sr.addResult(seq, 6, 7);
-    assertEquals("0a2cde5uv", sr.toString());
-  }
-
-  @Test(groups = { "Functional" })
-  public void testGetCharacters()
-  {
-    SequenceI seq = new Sequence("", "abcdefghijklm");
-    SearchResults sr = new SearchResults();
-    sr.addResult(seq, 1, 1);
-    assertEquals("a", sr.getCharacters());
-    sr.addResult(seq, 3, 5);
-    assertEquals("acde", sr.getCharacters());
-
-    seq = new Sequence("", "pqrstuvwxy");
-    sr.addResult(seq, 6, 7);
-    assertEquals("acdeuv", sr.getCharacters());
+    assertEquals("[Seq1/1-1, Seq1/3-5, Seq2/6-7]", sr.toString());
   }
 
   @Test(groups = { "Functional" })
   public void testEquals()
   {
     SequenceI seq1 = new Sequence("", "abcdefghijklm");
-    SearchResults sr1 = new SearchResults();
-    SearchResults sr2 = new SearchResults();
+    SearchResultsI sr1 = new SearchResults();
+    SearchResultsI sr2 = new SearchResults();
 
     assertFalse(sr1.equals(null)); // null object
     assertFalse(sr1.equals(seq1)); // wrong type
@@ -76,7 +62,7 @@ public class SearchResultsTest
     assertTrue(sr2.equals(sr1)); // reflexive
 
     /*
-     * only one result is not empty
+     * if only one result is not empty
      */
     sr1.addResult(seq1, 1, 1);
     assertTrue(sr1.equals(sr1));
@@ -111,8 +97,8 @@ public class SearchResultsTest
   {
     SequenceI seq1 = new Sequence("", "abcdefghijklm");
     SequenceI seq2 = new Sequence("", "abcdefghijklm");
-    SearchResults sr1 = new SearchResults();
-    SearchResults sr2 = new SearchResults();
+    SearchResultsI sr1 = new SearchResults();
+    SearchResultsI sr2 = new SearchResults();
 
     sr1.addResult(seq1, 1, 1);
     sr2.addResult(seq2, 1, 1);
@@ -127,8 +113,8 @@ public class SearchResultsTest
   public void testEquals_orderDiffers()
   {
     SequenceI seq1 = new Sequence("", "abcdefghijklm");
-    SearchResults sr1 = new SearchResults();
-    SearchResults sr2 = new SearchResults();
+    SearchResultsI sr1 = new SearchResults();
+    SearchResultsI sr2 = new SearchResults();
 
     sr1.addResult(seq1, 1, 1);
     sr1.addResult(seq1, 2, 2);
@@ -145,8 +131,8 @@ public class SearchResultsTest
   public void testHashcode()
   {
     SequenceI seq1 = new Sequence("", "abcdefghijklm");
-    SearchResults sr1 = new SearchResults();
-    SearchResults sr2 = new SearchResults();
+    SearchResultsI sr1 = new SearchResults();
+    SearchResultsI sr2 = new SearchResults();
 
     /*
      * both empty
@@ -178,7 +164,7 @@ public class SearchResultsTest
   public void testMatchConstructor()
   {
     SequenceI seq1 = new Sequence("", "abcdefghijklm");
-    Match m = new SearchResults().new Match(seq1, 2, 5);
+    SearchResultMatchI m = new SearchResults().new Match(seq1, 2, 5);
     assertSame(seq1, m.getSequence());
     assertEquals(2, m.getStart());
     assertEquals(5, m.getEnd());
@@ -189,4 +175,87 @@ public class SearchResultsTest
     assertEquals(2, m.getStart());
     assertEquals(5, m.getEnd());
   }
+
+  /**
+   * test markColumns for creating column selections
+   */
+  @Test(groups = { "Functional" })
+  public void testMarkColumns()
+  {
+    int marked = 0;
+    SequenceI seq1 = new Sequence("", "abcdefghijklm");
+    SequenceI seq2 = new Sequence("", "abcdefghijklm");
+    SequenceGroup s1g=new SequenceGroup(), s2g=new SequenceGroup(), sallg=new SequenceGroup();
+    s1g.addSequence(seq1, false);
+    s2g.addSequence(seq2, false);
+    sallg.addSequence(seq1, false);
+    sallg.addSequence(seq2, false);
+    
+    SearchResultsI sr = new SearchResults();
+    BitSet bs = new BitSet();
+    
+    SearchResultMatchI srm = null;
+    srm = sr.addResult(seq1, 1, 1);
+    Assert.assertNotNull("addResult didn't return Match", srm);
+    srm = sr.addResult(seq2, 1, 2);
+    assertEquals("Sequence reference not set", seq2, srm.getSequence());
+    assertEquals("match start incorrect", 1, srm.getStart());
+    assertEquals("match end incorrect", 2, srm.getEnd());
+    
+    // set start/end range for groups to cover matches
+
+    s1g.setStartRes(0);
+    s1g.setEndRes(5);
+    s2g.setStartRes(0);
+    s2g.setEndRes(5);
+    sallg.setStartRes(0);
+    sallg.setEndRes(5);
+
+    /*
+     * just seq1
+     */
+    marked = sr.markColumns(s1g, bs);
+    // check the bitset cardinality before checking the return value
+    assertEquals("Didn't mark expected number", 1, bs.cardinality());
+    assertEquals("Didn't return count of number of bits marked", 1, marked);
+    assertTrue("Didn't mark expected position", bs.get(0));
+    // now check return value for marking the same again
+    assertEquals(
+            "Didn't count number of bits marked for existing marked set",
+            0,
+            sr.markColumns(s1g, bs));
+    bs.clear();
+    
+    /*
+     * just seq2
+     */
+    marked = sr.markColumns(s2g, bs);
+    assertEquals("Didn't mark expected number", 2, bs.cardinality());
+    assertEquals("Didn't return count of number of bits marked", 2, marked);
+    assertTrue("Didn't mark expected position (1)", bs.get(0));
+    assertTrue("Didn't mark expected position (2)", bs.get(1));
+    
+    /*
+     * both seq1 and seq2 
+     * should be same as seq2
+     */
+    BitSet allbs = new BitSet();
+    assertEquals(2, sr.markColumns(sallg, allbs));
+    assertEquals(bs, allbs);
+
+    // now check range selection
+
+    /*
+     * limit s2g to just the second column, sallg to the first column
+     */
+    s2g.setStartRes(1);
+    s2g.setEndRes(1);
+    sallg.setEndRes(0);
+    BitSet tbs = new BitSet();
+    assertEquals("Group start/end didn't select columns to mark",1, sr.markColumns(s2g, tbs));
+    assertEquals("Group start/end didn't select columns to mark", 1, sr.markColumns(sallg, tbs));
+    assertEquals(
+            "Didn't set expected number of columns in total for two successive marks",
+            2, tbs.cardinality());
+  }
 }
index 00c52ed..dfe0e68 100644 (file)
@@ -35,6 +35,8 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.PDBEntry.Type;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.io.FileLoader;
@@ -347,4 +349,28 @@ public class AlignViewportTest
     af.getViewport().setGlobalColourScheme(cs);
     assertFalse(cs.conservationApplied());
   }
+
+  @Test(groups = { "Functional" })
+  public void testSetGetHasSearchResults()
+  {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", FormatAdapter.FILE);
+    SearchResultsI sr = new SearchResults();
+    SequenceI s1 = af.getViewport().getAlignment().getSequenceAt(0);
+
+    // create arbitrary range on first sequence
+    sr.addResult(s1, s1.getStart() + 10, s1.getStart() + 15);
+
+    // test set
+    af.getViewport().setSearchResults(sr);
+    // has -> true
+    assertTrue(af.getViewport().hasSearchResults());
+    // get == original
+    assertEquals(sr, af.getViewport().getSearchResults());
+
+    // set(null) results in has -> false
+
+    af.getViewport().setSearchResults(null);
+    assertFalse(af.getViewport().hasSearchResults());
+  }
 }
index 655aa2a..6d04661 100644 (file)
@@ -33,8 +33,8 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -81,9 +81,9 @@ public class MappingUtilsTest
     /*
      * Check protein residue 12 maps to codon 5-7, 13 to codon 8-10
      */
-    SearchResults sr = MappingUtils.buildSearchResults(aseq1, 12, acfList);
+    SearchResultsI sr = MappingUtils.buildSearchResults(aseq1, 12, acfList);
     assertEquals(1, sr.getResults().size());
-    Match m = sr.getResults().get(0);
+    SearchResultMatchI m = sr.getResults().get(0);
     assertEquals(seq1.getDatasetSequence(), m.getSequence());
     assertEquals(5, m.getStart());
     assertEquals(7, m.getEnd());
@@ -134,9 +134,9 @@ public class MappingUtilsTest
     /*
      * Check protein residue 8 maps to [6, 8, 9]
      */
-    SearchResults sr = MappingUtils.buildSearchResults(aseq1, 8, acfList);
+    SearchResultsI sr = MappingUtils.buildSearchResults(aseq1, 8, acfList);
     assertEquals(2, sr.getResults().size());
-    Match m = sr.getResults().get(0);
+    SearchResultMatchI m = sr.getResults().get(0);
     assertEquals(seq1.getDatasetSequence(), m.getSequence());
     assertEquals(6, m.getStart());
     assertEquals(6, m.getEnd());