From: gmungoc Date: Wed, 23 Nov 2016 10:03:07 +0000 (+0000) Subject: Merge branch 'develop' into features/JAL-2295setChimeraAttributes X-Git-Tag: Release_2_11_0~62^2~42 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=7d8f40032746ee633dd980d5609c4d517a40421a;hp=8e5fa38447bcb8541c71bd7491c16a96f80fabc2;p=jalview.git Merge branch 'develop' into features/JAL-2295setChimeraAttributes Conflicts: src/jalview/structure/StructureSelectionManager.java --- diff --git a/THIRDPARTYLIBS b/THIRDPARTYLIBS index 7d12783..85aa587 100644 --- a/THIRDPARTYLIBS +++ b/THIRDPARTYLIBS @@ -6,8 +6,11 @@ A number of sources have also been adapted for incorporation into Jalview's sour ext.edu.ucsf.rbvi.strucviz2 includes sources originally developed by Scooter Morris and Nadezhda Doncheva for the Cytoscape StructureViz2 plugin. It is released under the Berkley license and we hereby acknowledge its original copyright is held by the UCSF Computer Graphics Laboratory and the software was developed with support by the NIH National Center for Research Resources, grant P41-RR01081. + + jalview.ext.android includes code taken from the Android Open Source Project (https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/util). + The Apache 2.0 Licence (http://www.apache.org/licenses/LICENSE-2.0) is acknowledged in the source code. -Licencing information for each library is given below: +Licensing information for each library is given below: JGoogleAnalytics_0.3.jar APL 2.0 License - http://code.google.com/p/jgoogleanalytics/ Jmol-14.2.14_2015.06.11.jar GPL/LGPLv2 http://sourceforge.net/projects/jmol/files/ diff --git a/help/html/colourSchemes/abovePID.html b/help/html/colourSchemes/abovePID.html index ad47dae..8adacc9 100755 --- a/help/html/colourSchemes/abovePID.html +++ b/help/html/colourSchemes/abovePID.html @@ -35,11 +35,10 @@ td { Colouring above a percentage identity threshold

Selecting this option causes the colour scheme to be applied to - only those residues that occur in that column more than a certain + only those residues that occur in that column at least a certain percentage of the time. For instance, selecting the threshold to be - 100 will only colour those columns with 100% identity.

-

To be coloured, a residue must match the consensus (most commonly occurring) residue for the column (or joint equal consensus).

+ 100 will only colour those columns with 100% identity, and selecting 50 will shade residues appearing in least 50% of the rows (or sequences) in each column.

The percentage calculation may include or exclude gaps in the column, depending on the option selected for the consensus calculation.

-

With a threshold of 0, colouring is unchanged (including non-consensus residues).

+

With a threshold of 0, colouring is unchanged.

diff --git a/help/html/features/chimera.html b/help/html/features/chimera.html index 1b0b9c1..cbef2c1 100644 --- a/help/html/features/chimera.html +++ b/help/html/features/chimera.html @@ -65,9 +65,16 @@ 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. +

For comprehensive details of Chimera's commands, refer to the + tool's Help menu.

+

+ Selecting residues in Jalview from Chimera
+ When a selection is highlighted in a Jalview window, use the + Select→Select Highlighted Region or press B + to add the mapped positions to the alignment window's column + selection. +

Basic screen operations (see Chimera diff --git a/help/html/features/clarguments.html b/help/html/features/clarguments.html index 4b4aab9..e065494 100644 --- a/help/html/features/clarguments.html +++ b/help/html/features/clarguments.html @@ -258,6 +258,14 @@

Create Scalable Vector Graphics file FILE from alignment.
+ +
-biojsMSA FILE
+
Write an HTML page to display + the alignment with the + BioJS MSAviewer MSA +
+ + diff --git a/help/html/features/jmol.html b/help/html/features/jmol.html index 2f10196..0cd6168 100644 --- a/help/html/features/jmol.html +++ b/help/html/features/jmol.html @@ -74,15 +74,18 @@ based structure superposition was added in Jalview 2.6

- Controls
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. + Controls
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 + Select→Select Highlighted columns from any linked + alignment window to mark the columns highlighted after mousing over + the structure.

Selecting a residue highlights its associated sequence residue and alpha carbon location. Double clicking an atom allows distances diff --git a/help/html/features/search.html b/help/html/features/search.html index 796d623..a8238eb 100755 --- a/help/html/features/search.html +++ b/help/html/features/search.html @@ -65,6 +65,17 @@ td { Settings" under the "View" menu to change the visibility and colour of the new sequence feature.

+

+ Selecting regions from Search Results +

+

+ Press 'B' or select the Select Highlighted Columns option + from the alignment window's select menu to add columns containing + highlighted search results to the alignment window's column + selection. +

+

+ A quick Regular Expression Guide

A regular expression is not just a simple text query - although diff --git a/help/html/io/exportseqreport.html b/help/html/io/exportseqreport.html index ba97557..3f6a058 100644 --- a/help/html/io/exportseqreport.html +++ b/help/html/io/exportseqreport.html @@ -28,16 +28,16 @@ Much of the information retrieved by Jalview about a sequence is visualized on the alignment. Often, however, there are a huge number of ontology terms, cross-references, links to publications and other - kinds of data shown in the sequence ID tooltip that cannot be - examined. In this case, you can view and export the information - shown in a sequence's ID tooltip by right-clicking and selecting the + kinds of data associated with a sequence, and only some of these are shown in + sequence ID tooltip. To show the full set of annotation and database links for + a sequence, right-click and select the "(sequence's name)→Sequence Details ..." entry from the pop-up menu.

Annotation Reports for a range of sequences
- If you would like to view the tooltips for a number of sequences, + If you would like to view database and metadata for a number of sequences, simply select them all and then use the Selection→Sequence Details ... entry in the pop-up menu. diff --git a/help/html/keys.html b/help/html/keys.html index 2ba9a49..b79ce4d 100755 --- a/help/html/keys.html +++ b/help/html/keys.html @@ -167,6 +167,19 @@ columns are selected, you should use the H Both Launches the search window + B + Both + Mark the currently highlighted columns + + Alt 'B' + Both + Mark all but the currently highlighted columns + + Control 'B' + Both + Toggle the marks on the currently highlighted + columns (or all others if Alt is pressed) + H Both diff --git a/help/html/menus/alignmentMenu.html b/help/html/menus/alignmentMenu.html index c8b2270..167cb25 100755 --- a/help/html/menus/alignmentMenu.html +++ b/help/html/menus/alignmentMenu.html @@ -278,6 +278,12 @@ Columns by Annotation
Select or Hide columns in the alignment according to secondary structure, labels and values shown in alignment annotation rows. +

  • Select Highlighted Columns
    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.
  • View diff --git a/help/html/releases.html b/help/html/releases.html index 3fe08cb..2d0c4e8 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -47,6 +47,73 @@
    + 2.10.1
    + 24/11/2016
    +
    + +
    + General +
      +
    • Improved memory usage: sparse arrays used for all consensus calculations
    • +
    • +
    + Application +
      +
    • Sequence ID tool tips have been tamed (databases sorted alphabetically, abridged ID sets)
    • +
    • New replacement token for creating URLs just from database cross references. Users with custom links will receive a warning dialog asking them to update their preferences.
    • +
    • Cancel button and escape listener on dialog warning user about disconnecting Jalview from a Chimera session
    • +
    • Custom URL links for database cross-references are matched to database name regardless of case
    • +
    • + + +
    + Applet +
      +
    + Build and deployment +
      +
    • +
    +
    + + +
    + General +
      +
    • Columns with more than one modal residue are not coloured or thresholded according to percent identity (first observed in Jalview 2.8.2)
    • +
    • Threonine incorrectly reported as not hydrophobic
    • +
    • Updates to documentation pages (above PID threshold, amino acid properties)
    • +
    • Lower case residues in sequences are not reported as mapped to residues in a structure file in the View Mapping report
    • +
    + Application +
      +
    • Custom URL links for specific database names without regular expressions also offer invalid links from Sequence ID
    • +
    • Removing a single configured link in the URL links pane in Connections preferences doesn't actually update Jalview configuration
    • +
    • CTRL-Click on a selected region to open the alignment area popup menu doesn't work on El-Capitan
    • +
    • Jalview doesn't offer to associate mmCIF files with similarly named sequences if dropped onto the alignment
    • +
    • Additional mappings are shown for PDB entries where more chains exist in the PDB accession than are reported in the SIFTS file
    • +
    • Certain structures do not get mapped to the structure view when displayed with Chimera
    • +
    • No chains shown in the Chimera view panel's View->Show Chains submenu
    • + +
    • +
    • +
    + Applet +
      +
    + Build and deployment +
      +
    • Failing/passing unit tests
    • +
    + New Known Issues +
      +
    • +
    +
    + + + +
    2.10.0b1
    25/10/2016
    @@ -402,6 +469,10 @@ Quality and Conservation are now shown on load even when Consensus calculation is disabled
  • +
  • + Remove right on penultimate column of + alignment does nothing +
  • Application @@ -616,6 +695,9 @@ Applet Build and Deployment +
    diff --git a/help/html/webServices/dbreffetcher.html b/help/html/webServices/dbreffetcher.html index 52220d2..83c80ba 100644 --- a/help/html/webServices/dbreffetcher.html +++ b/help/html/webServices/dbreffetcher.html @@ -25,10 +25,12 @@

    Discovering Database References for Sequences
    - Database references are associated with a sequence are displayed as a - list in the tooltip shown when mousing over its sequence ID. Jalview - uses references for the retrieval of PDB structures and Sequence Details window. . + Jalview also uses references for the retrieval of + PDB structures and DAS features, and for retrieving sequence cross-references such as the protein products of a DNA sequence. diff --git a/help/html/webServices/urllinks.html b/help/html/webServices/urllinks.html index 1a1f0d0..36c7c6b 100644 --- a/help/html/webServices/urllinks.html +++ b/help/html/webServices/urllinks.html @@ -66,6 +66,12 @@ the sequence ID for the sequence (since Jalview 2.10.1).

    + If Jalview opens a project with links which include $SEQUENCE_ID$ tokens, it will present + the user with a warning message, as these links may need to be updated to use $DB_ACCESSION$, if + they were added before Jalview 2.10.1. The message lists the links which should be reviewed. + The warning can be turned off completely via a checkbox in the message dialog. +

    +

    Regular Expression Substitution
    A url may contain a string of the form $SEQUENCE_ID=/regular expression/=$ or $DB_ACCESSION=/regular expression/=$. diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 1262536..b9d1cf0 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -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 @@ -1274,3 +1276,4 @@ label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ is no longer used for DB access label.SEQUENCE_ID_for_DB_ACCESSION1 = Please review your URL links in the 'Connections' tab of the Preferences window: label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'. label.do_not_display_again = Do not display this message again +label.output_seq_details = Output Sequence Details to list all database references diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 96c8ef2..9c2436c 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -1272,4 +1272,5 @@ label.operation_failed = Operaci label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ no se utiliza más para accesiones DB label.SEQUENCE_ID_for_DB_ACCESSION1 = Por favor, revise sus URLs en la pestaña 'Conexiones' de la ventana de Preferencias: label.SEQUENCE_ID_for_DB_ACCESSION2 = URL enlaza usando '$SEQUENCE_ID$' para accesiones DB ahora usar '$DB_ACCESSION$'. -label.do_not_display_again = No mostrar este mensaje de nuevo \ No newline at end of file +label.do_not_display_again = No mostrar este mensaje de nuevo +label.output_seq_details = Seleccionar Detalles de la secuencia para ver todas diff --git a/src/jalview/analysis/AAFrequency.java b/src/jalview/analysis/AAFrequency.java index 6bdffe1..17874e6 100755 --- a/src/jalview/analysis/AAFrequency.java +++ b/src/jalview/analysis/AAFrequency.java @@ -26,9 +26,11 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; import jalview.datamodel.Profile; import jalview.datamodel.ProfileI; +import jalview.datamodel.Profiles; +import jalview.datamodel.ProfilesI; import jalview.datamodel.ResidueCount; -import jalview.datamodel.SequenceI; import jalview.datamodel.ResidueCount.SymbolCounts; +import jalview.datamodel.SequenceI; import jalview.ext.android.SparseIntArray; import jalview.util.Comparison; import jalview.util.Format; @@ -65,13 +67,13 @@ public class AAFrequency } } - public static final ProfileI[] calculate(List list, + public static final ProfilesI calculate(List list, int start, int end) { return calculate(list, start, end, false); } - public static final ProfileI[] calculate(List sequences, + public static final ProfilesI calculate(List sequences, int start, int end, boolean profile) { SequenceI[] seqs = new SequenceI[sequences.size()]; @@ -81,20 +83,19 @@ public class AAFrequency for (int i = 0; i < sequences.size(); i++) { seqs[i] = sequences.get(i); - if (seqs[i].getLength() > width) + int length = seqs[i].getLength(); + if (length > width) { - width = seqs[i].getLength(); + width = length; } } - ProfileI[] reply = new ProfileI[width]; - if (end >= width) { end = width; } - calculate(seqs, start, end, reply, profile); + ProfilesI reply = calculate(seqs, width, start, end, profile); return reply; } } @@ -103,17 +104,17 @@ public class AAFrequency * Calculate the consensus symbol(s) for each column in the given range. * * @param sequences + * @param width + * the full width of the alignment * @param start * start column (inclusive, base zero) * @param end * end column (exclusive) - * @param result - * array in which to store profile per column * @param saveFullProfile * if true, store all symbol counts */ - public static final void calculate(final SequenceI[] sequences, - int start, int end, ProfileI[] result, boolean saveFullProfile) + public static final ProfilesI calculate(final SequenceI[] sequences, + int width, int start, int end, boolean saveFullProfile) { // long now = System.currentTimeMillis(); int seqCount = sequences.length; @@ -121,6 +122,8 @@ public class AAFrequency int nucleotideCount = 0; int peptideCount = 0; + ProfileI[] result = new ProfileI[width]; + for (int column = start; column < end; column++) { /* @@ -183,6 +186,7 @@ public class AAFrequency result[column] = profile; } + return new Profiles(result); // long elapsed = System.currentTimeMillis() - now; // System.out.println(elapsed); } @@ -221,10 +225,10 @@ public class AAFrequency * the annotation row to add annotations to * @param profiles * the source consensus data - * @param iStart - * start column - * @param width - * end column + * @param startCol + * start column (inclusive) + * @param endCol + * end column (exclusive) * @param ignoreGaps * if true, normalise residue percentages ignoring gaps * @param showSequenceLogo @@ -234,12 +238,12 @@ public class AAFrequency * number of sequences */ public static void completeConsensus(AlignmentAnnotation consensus, - ProfileI[] profiles, int iStart, int width, boolean ignoreGaps, + ProfilesI profiles, int startCol, int endCol, boolean ignoreGaps, boolean showSequenceLogo, long nseq) { // long now = System.currentTimeMillis(); if (consensus == null || consensus.annotations == null - || consensus.annotations.length < width) + || consensus.annotations.length < endCol) { /* * called with a bad alignment annotation row @@ -248,21 +252,21 @@ public class AAFrequency return; } - final int dp = getPercentageDp(nseq); - - for (int i = iStart; i < width; i++) + for (int i = startCol; i < endCol; i++) { - ProfileI profile; - if (i >= profiles.length || ((profile = profiles[i]) == null)) + ProfileI profile = profiles.get(i); + if (profile == null) { /* * happens if sequences calculated over were * shorter than alignment width */ consensus.annotations[i] = null; - continue; + return; } + final int dp = getPercentageDp(nseq); + float value = profile.getPercentageIdentity(ignoreGaps); String description = getTooltip(profile, value, showSequenceLogo, @@ -277,8 +281,8 @@ public class AAFrequency { modalResidue = "+"; } - consensus.annotations[i] = new Annotation(modalResidue, - description, ' ', value); + consensus.annotations[i] = new Annotation(modalResidue, description, + ' ', value); } // long elapsed = System.currentTimeMillis() - now; // System.out.println(-elapsed); diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java index 72097e0..25ee7d2 100644 --- a/src/jalview/analysis/Finder.java +++ b/src/jalview/analysis/Finder.java @@ -21,24 +21,31 @@ 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 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(); 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 spaces = new ArrayList(); 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 getIdMatch() { return idMatch; } @@ -338,7 +348,7 @@ public class Finder /** * @return the searchResults */ - public SearchResults getSearchResults() + public SearchResultsI getSearchResults() { return searchResults; } diff --git a/src/jalview/api/AlignViewControllerI.java b/src/jalview/api/AlignViewControllerI.java index 26966ba..8ed2c95 100644 --- a/src/jalview/api/AlignViewControllerI.java +++ b/src/jalview/api/AlignViewControllerI.java @@ -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); + } diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index e30a052..72542b3 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -26,7 +26,8 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; -import jalview.datamodel.ProfileI; +import jalview.datamodel.ProfilesI; +import jalview.datamodel.SearchResultsI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; @@ -82,7 +83,7 @@ public interface AlignViewportI extends ViewStyleI ColumnSelection getColumnSelection(); - ProfileI[] getSequenceConsensusHash(); + ProfilesI getSequenceConsensusHash(); /** * Get consensus data table for the cDNA complement of this alignment (if any) @@ -145,7 +146,7 @@ public interface AlignViewportI extends ViewStyleI * * @param hconsensus */ - void setSequenceConsensusHash(ProfileI[] hconsensus); + void setSequenceConsensusHash(ProfilesI hconsensus); /** * Set the cDNA complement consensus for the viewport @@ -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(); } diff --git a/src/jalview/api/FeatureRenderer.java b/src/jalview/api/FeatureRenderer.java index dbc9880..f54231e 100644 --- a/src/jalview/api/FeatureRenderer.java +++ b/src/jalview/api/FeatureRenderer.java @@ -132,7 +132,8 @@ public interface FeatureRenderer void setGroupVisibility(String group, boolean visible); /** - * locate features at a particular position on the given sequence + * Returns features at the specified position on the given sequence. + * Non-positional features are not included. * * @param sequence * @param res diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index 45e39cc..160224b 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -802,7 +802,7 @@ public class APopupMenu extends java.awt.PopupMenu implements CutAndPasteTransfer cap = new CutAndPasteTransfer(false, ap.alignFrame); - StringBuffer contents = new StringBuffer(); + StringBuilder contents = new StringBuilder(128); for (SequenceI seq : sequences) { contents.append(MessageManager.formatMessage( @@ -813,7 +813,6 @@ public class APopupMenu extends java.awt.PopupMenu implements seq, true, true, - false, (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr .getMinMax() : null); contents.append("

    "); diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index e5178cb..4bd77b6 100644 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -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()) { diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index 813ab84..e97c347 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -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 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() diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index 82736d7..2fca07d 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -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()); diff --git a/src/jalview/appletgui/Finder.java b/src/jalview/appletgui/Finder.java index 75d9b9e..d2fe69c 100644 --- a/src/jalview/appletgui/Finder.java +++ b/src/jalview/appletgui/Finder.java @@ -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 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); diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index 7216bfe..5d6bb07 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -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(); } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index 6ca9499..8d6e683 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -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()) { diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java index f508bc3..9451c3b 100644 --- a/src/jalview/controller/AlignViewController.java +++ b/src/jalview/controller/AlignViewController.java @@ -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; + } + } diff --git a/src/jalview/datamodel/AlignedCodonFrame.java b/src/jalview/datamodel/AlignedCodonFrame.java index c5204eb..4fbfd62 100644 --- a/src/jalview/datamodel/AlignedCodonFrame.java +++ b/src/jalview/datamodel/AlignedCodonFrame.java @@ -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(); diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 2289ac6..bd78827 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -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; diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java index 1d37fa6..7274e5f 100755 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@ -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/Profiles.java b/src/jalview/datamodel/Profiles.java new file mode 100644 index 0000000..98a8c6d --- /dev/null +++ b/src/jalview/datamodel/Profiles.java @@ -0,0 +1,43 @@ +package jalview.datamodel; + +public class Profiles implements ProfilesI +{ + + private ProfileI[] profiles; + + public Profiles(ProfileI[] p) + { + profiles = p; + } + + /** + * Returns the profile for the given column, or null if none found + * + * @param col + */ + @Override + public ProfileI get(int col) + { + return profiles != null && col >= 0 && col < profiles.length ? profiles[col] + : null; + } + + /** + * Returns the first column (base 0) covered by the profiles + */ + @Override + public int getStartColumn() + { + return 0; + } + + /** + * Returns the last column (base 0) covered by the profiles + */ + @Override + public int getEndColumn() + { + return profiles == null ? 0 : profiles.length - 1; + } + +} diff --git a/src/jalview/datamodel/ProfilesI.java b/src/jalview/datamodel/ProfilesI.java new file mode 100644 index 0000000..6719de6 --- /dev/null +++ b/src/jalview/datamodel/ProfilesI.java @@ -0,0 +1,12 @@ +package jalview.datamodel; + +public interface ProfilesI +{ + + ProfileI get(int i); + + int getStartColumn(); + + int getEndColumn(); + +} diff --git a/src/jalview/datamodel/SearchResultMatchI.java b/src/jalview/datamodel/SearchResultMatchI.java new file mode 100644 index 0000000..732f1dc --- /dev/null +++ b/src/jalview/datamodel/SearchResultMatchI.java @@ -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 diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java index b9db461..1bf5475 100755 --- a/src/jalview/datamodel/SearchResults.java +++ b/src/jalview/datamodel/SearchResults.java @@ -21,26 +21,26 @@ 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 matches = new ArrayList(); + private List matches = new ArrayList(); /** * 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 getResults() + @Override + public List 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) 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 index 0000000..93183f2 --- /dev/null +++ b/src/jalview/datamodel/SearchResultsI.java @@ -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. + *

    + * 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 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 diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index ca90003..9245761 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -527,7 +527,7 @@ public class SequenceGroup implements AnnotatedCollectionI boolean upd = false; try { - ProfileI[] cnsns = AAFrequency.calculate(sequences, startRes, + ProfilesI cnsns = AAFrequency.calculate(sequences, startRes, endRes + 1, showSequenceLogo); if (consensus != null) { @@ -599,9 +599,9 @@ public class SequenceGroup implements AnnotatedCollectionI c.completeAnnotations(conservation, null, startRes, endRes + 1); } - public ProfileI[] consensusData = null; + public ProfilesI consensusData = null; - private void _updateConsensusRow(ProfileI[] cnsns, long nseq) + private void _updateConsensusRow(ProfilesI cnsns, long nseq) { if (consensus == null) { diff --git a/src/jalview/ext/android/ContainerHelpers.java b/src/jalview/ext/android/ContainerHelpers.java index f536819..4033dcc 100644 --- a/src/jalview/ext/android/ContainerHelpers.java +++ b/src/jalview/ext/android/ContainerHelpers.java @@ -16,6 +16,12 @@ package jalview.ext.android; * limitations under the License. */ +/* + * Copied to Jalview September 2016. + * Only the members of this class required for SparseIntArray were copied. + * Method binarySearch(short[] array, int size, short value) added to support + * SparseShortArray. + */ class ContainerHelpers { static final boolean[] EMPTY_BOOLEANS = new boolean[0]; @@ -83,7 +89,7 @@ class ContainerHelpers while (lo <= hi) { final int mid = (lo + hi) >>> 1; - final int midVal = array[mid]; + final short midVal = array[mid]; if (midVal < value) { lo = mid + 1; diff --git a/src/jalview/ext/android/SparseIntArray.java b/src/jalview/ext/android/SparseIntArray.java index 2b9c4af..fcd4f1f 100644 --- a/src/jalview/ext/android/SparseIntArray.java +++ b/src/jalview/ext/android/SparseIntArray.java @@ -40,6 +40,13 @@ package jalview.ext.android; * order in the case of valueAt(int). *

    */ + +/* + * Imported into Jalview September 2016 + * Change log: + * Sep 2016 method add(int, int) added for more efficient increment of counts + * (a single binary search, rather than one on read and one on write) + */ public class SparseIntArray implements Cloneable { private int[] mKeys; diff --git a/src/jalview/ext/android/SparseShortArray.java b/src/jalview/ext/android/SparseShortArray.java index 375d745..f961f55 100644 --- a/src/jalview/ext/android/SparseShortArray.java +++ b/src/jalview/ext/android/SparseShortArray.java @@ -40,12 +40,15 @@ package jalview.ext.android; * order in the case of valueAt(int). *

    */ -/** - * A copy of SparseShortArray designed to store short values (to minimise space - * usage). + +/* + * Added to Jalview September 2016. A copy of SparseIntArray designed to store + * short values (to minimise space usage). *

    - * Note that operations append, put, add throw ArithmeticException if the - * resulting value overflows the range of a short. + * Note that operations append, put, add throw ArithmeticException if either the + * key or the resulting value overflows the range of a short. Calling code + * should trap and handle this, for example by switching to using a + * SparseIntArray instead. */ public class SparseShortArray implements Cloneable { diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 7e70fef..cc1de6a 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -27,8 +27,8 @@ import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; +import jalview.datamodel.SearchResultMatchI; import jalview.datamodel.SearchResults; -import jalview.datamodel.SearchResults.Match; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.httpserver.AbstractRequestHandler; @@ -1260,7 +1260,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel * expect one matched alignment position, or none * (if the structure position is not mapped) */ - for (Match m : sr.getResults()) + for (SearchResultMatchI m : sr.getResults()) { SequenceI seq = m.getSequence(); int start = m.getStart(); diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 1372749..6a637e3 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -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 diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index d0a0f11..03aee5d 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -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()) { diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 9f1162f..dc1f95b 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -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; @@ -960,7 +960,7 @@ public class AlignmentPanel extends GAlignmentPanel implements } else { - return printUnwrapped(pwidth, pheight, pi, pg); + return printUnwrapped(pwidth, pheight, pi, pg, pg); } } @@ -981,110 +981,103 @@ public class AlignmentPanel extends GAlignmentPanel implements * @throws PrinterException * DOCUMENT ME! */ - public int printUnwrapped(int pwidth, int pheight, int pi, - Graphics... pg) + /** + * Draws the alignment image, including sequence ids, sequences, and + * annotation labels and annotations if shown, on either one or two Graphics + * context. + * + * @param pageWidth + * @param pageHeight + * @param pi + * @param idGraphics + * the graphics context for sequence ids and annotation labels + * @param alignmentGraphics + * the graphics context for sequences and annotations (may or may not + * be the same context as idGraphics) + * @return + * @throws PrinterException + */ + public int printUnwrapped(int pageWidth, int pageHeight, int pi, + Graphics idGraphics, Graphics alignmentGraphics) throws PrinterException { - boolean isMultiGraphics = pg.length > 1; - int G0 = 0; // Graphic index of idPanel graphics in multi-graphics mode or - // entire graphics for non mulit-graphics mode - int G1 = 0; // Graphic index of alignmentPanel graphics for multi-graphics - // mode - if (isMultiGraphics) - { - G0 = 0; - G1 = 1; - } - - int idWidth = getVisibleIdWidth(false); - FontMetrics fm = getFontMetrics(av.getFont()); - int scaleHeight = av.getCharHeight() + fm.getDescent(); - - pg[G0].setColor(Color.white); - pg[G0].fillRect(0, 0, pwidth, pheight); - pg[G0].setFont(av.getFont()); + final int idWidth = getVisibleIdWidth(false); - // ////////////////////////////////// - // / How many sequences and residues can we fit on a printable page? - int totalRes = (pwidth - idWidth) / av.getCharWidth(); + /* + * Get the horizontal offset to where we draw the sequences. + * This is idWidth if using a single Graphics context, else zero. + */ + final int alignmentGraphicsOffset = idGraphics != alignmentGraphics ? 0 : idWidth; - int totalSeq = (pheight - scaleHeight) / av.getCharHeight() - 1; + FontMetrics fm = getFontMetrics(av.getFont()); + int charHeight = av.getCharHeight(); + int scaleHeight = charHeight + fm.getDescent(); - int pagesWide = (av.getAlignment().getWidth() / totalRes) + 1; + idGraphics.setColor(Color.white); + idGraphics.fillRect(0, 0, pageWidth, pageHeight); + idGraphics.setFont(av.getFont()); - // /////////////////////////// - // / Only print these sequences and residues on this page - int startRes; + /* + * How many sequences and residues can we fit on a printable page? + */ + int totalRes = (pageWidth - idWidth) / av.getCharWidth(); - // /////////////////////////// - // / Only print these sequences and residues on this page - int endRes; + int totalSeq = (pageHeight - scaleHeight) / charHeight - 1; - // /////////////////////////// - // / Only print these sequences and residues on this page - int startSeq; + int alignmentWidth = av.getAlignment().getWidth(); + int pagesWide = (alignmentWidth / totalRes) + 1; - // /////////////////////////// - // / Only print these sequences and residues on this page - int endSeq; - startRes = (pi % pagesWide) * totalRes; - endRes = (startRes + totalRes) - 1; + final int startRes = (pi % pagesWide) * totalRes; + int endRes = (startRes + totalRes) - 1; - if (endRes > (av.getAlignment().getWidth() - 1)) + if (endRes > (alignmentWidth - 1)) { - endRes = av.getAlignment().getWidth() - 1; + endRes = alignmentWidth - 1; } - startSeq = (pi / pagesWide) * totalSeq; - endSeq = startSeq + totalSeq; + final int startSeq = (pi / pagesWide) * totalSeq; + int endSeq = startSeq + totalSeq; - if (endSeq > av.getAlignment().getHeight()) + int alignmentHeight = av.getAlignment().getHeight(); + if (endSeq > alignmentHeight) { - endSeq = av.getAlignment().getHeight(); + endSeq = alignmentHeight; } - int pagesHigh = ((av.getAlignment().getHeight() / totalSeq) + 1) - * pheight; + int pagesHigh = ((alignmentHeight / totalSeq) + 1) + * pageHeight; if (av.isShowAnnotation()) { pagesHigh += getAnnotationPanel().adjustPanelHeight() + 3; } - pagesHigh /= pheight; + pagesHigh /= pageHeight; if (pi >= (pagesWide * pagesHigh)) { return Printable.NO_SUCH_PAGE; } + final int alignmentDrawnHeight = (endSeq - startSeq) * charHeight + + 3; - // draw Scale - if (isMultiGraphics) - { - pg[G1].translate(0, 0); - getScalePanel().drawScale(pg[G1], startRes, endRes, - pwidth - idWidth, scaleHeight); - pg[G1].translate(-idWidth, scaleHeight); - } - else - { - pg[G0].translate(idWidth, 0); - getScalePanel().drawScale(pg[G0], startRes, endRes, pwidth - idWidth, - scaleHeight); - pg[G0].translate(-idWidth, scaleHeight); - } + /* + * draw the Scale at horizontal offset, then reset to top left (0, 0) + */ + alignmentGraphics.translate(alignmentGraphicsOffset, 0); + getScalePanel().drawScale(alignmentGraphics, startRes, endRes, + pageWidth - idWidth, scaleHeight); + alignmentGraphics.translate(-alignmentGraphicsOffset, 0); - // ////////////// - // Draw the ids + /* + * Draw the sequence ids, offset for scale height, + * then reset to top left (0, 0) + */ + idGraphics.translate(0, scaleHeight); + idGraphics.setFont(getIdPanel().getIdCanvas().getIdfont()); Color currentColor = null; Color currentTextColor = null; - if (isMultiGraphics) - { - pg[G0].translate(0, scaleHeight); - } - pg[G0].setFont(getIdPanel().getIdCanvas().getIdfont()); - SequenceI seq; for (int i = startSeq; i < endSeq; i++) { @@ -1092,6 +1085,9 @@ public class AlignmentPanel extends GAlignmentPanel implements if ((av.getSelectionGroup() != null) && av.getSelectionGroup().getSequences(null).contains(seq)) { + /* + * gray out ids of sequences in selection group (if any) + */ currentColor = Color.gray; currentTextColor = Color.black; } @@ -1101,70 +1097,58 @@ public class AlignmentPanel extends GAlignmentPanel implements currentTextColor = Color.black; } - pg[G0].setColor(currentColor); - pg[G0].fillRect(0, (i - startSeq) * av.getCharHeight(), idWidth, - av.getCharHeight()); + idGraphics.setColor(currentColor); + idGraphics.fillRect(0, (i - startSeq) * charHeight, idWidth, + charHeight); - pg[G0].setColor(currentTextColor); + idGraphics.setColor(currentTextColor); int xPos = 0; + String displayId = seq.getDisplayId(av.getShowJVSuffix()); if (av.isRightAlignIds()) { - fm = pg[G0].getFontMetrics(); + fm = idGraphics.getFontMetrics(); xPos = idWidth - - fm.stringWidth(seq.getDisplayId(av.getShowJVSuffix())) + - fm.stringWidth(displayId) - 4; } - pg[G0].drawString(seq.getDisplayId(av.getShowJVSuffix()), xPos, - (((i - startSeq) * av.getCharHeight()) + av.getCharHeight()) - - (av.getCharHeight() / 5)); + idGraphics.drawString(displayId, xPos, + (((i - startSeq) * charHeight) + charHeight) + - (charHeight / 5)); } + idGraphics.setFont(av.getFont()); + idGraphics.translate(0, -scaleHeight); - pg[G0].setFont(av.getFont()); - + /* + * draw the sequences, offset for scale height, and id width (if using a + * single graphics context), then reset to (0, scale height) + */ + alignmentGraphics.translate(alignmentGraphicsOffset, scaleHeight); + getSeqPanel().seqCanvas.drawPanel(alignmentGraphics, startRes, endRes, + startSeq, endSeq, 0); + alignmentGraphics.translate(-alignmentGraphicsOffset, 0); - // draw main sequence panel - pg[G0].translate(idWidth, 0); - if (isMultiGraphics) + if (av.isShowAnnotation() && (endSeq == alignmentHeight)) { - pg[G1].translate(idWidth, 0); - getSeqPanel().seqCanvas.drawPanel(pg[G1], startRes, endRes, - startSeq, endSeq, 0); - } - else - { - getSeqPanel().seqCanvas.drawPanel(pg[G0], startRes, endRes, startSeq, - endSeq, 0); - } + /* + * draw annotation labels; drawComponent() translates by + * getScrollOffset(), so compensate for that first; + * then reset to (0, scale height) + */ + int offset = getAlabels().getScrollOffset(); + idGraphics.translate(0, -offset); + idGraphics.translate(0, alignmentDrawnHeight); + getAlabels().drawComponent(idGraphics, idWidth); + idGraphics.translate(0, -alignmentDrawnHeight); - if (av.isShowAnnotation() && (endSeq == av.getAlignment().getHeight())) - { - // draw annotation label - need to offset for current scroll position - int offset = -getAlabels().getScrollOffset(); - pg[G0].translate(0, offset); - pg[G0].translate(-idWidth - 3, - (endSeq - startSeq) * av.getCharHeight() + 3); - getAlabels().drawComponent(pg[G0], idWidth); - pg[G0].translate(idWidth + 3, 0); - pg[G0].translate(0, -offset); - if (isMultiGraphics) - { - // draw annotation - need to offset for current scroll position - pg[G1].translate(0, offset); - pg[G1].translate(-idWidth - 3, - (endSeq - startSeq) * av.getCharHeight() + 3); - pg[G1].translate(idWidth + 3, 0); - getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), - av, pg[G1], -1, startRes, endRes + 1); - pg[G1].translate(0, -offset); - } - else - { - getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), - av, pg[G0], -1, startRes, endRes + 1); - pg[G0].translate(0, -offset); - } + /* + * draw the annotations starting at + * (idOffset, alignmentHeight) from (0, scaleHeight) + */ + alignmentGraphics.translate(alignmentGraphicsOffset, alignmentDrawnHeight); + getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av, + alignmentGraphics, -1, startRes, endRes + 1); } return Printable.PAGE_EXISTS; @@ -1363,22 +1347,23 @@ public class AlignmentPanel extends GAlignmentPanel implements aDimension.getWidth(), aDimension.getHeight() + boarderBottomOffset, file, imageTitle, alignFrame, pSessionId, headless); + Graphics graphics = im.getGraphics(); if (av.getWrapAlignment()) { - if (im.getGraphics() != null) + if (graphics != null) { printWrappedAlignment(aDimension.getWidth(), aDimension.getHeight() + boarderBottomOffset, 0, - im.getGraphics()); + graphics); im.writeImage(); } } else { - if (im.getGraphics() != null) + if (graphics != null) { printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), - 0, im.getGraphics()); + 0, graphics, graphics); im.writeImage(); } } @@ -1840,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 seqs = av.getAlignment().getSequences(); /* diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java index 426ea32..83d1612 100644 --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@ -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()); diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java index 6bff69a..a8dbc38 100755 --- a/src/jalview/gui/Finder.java +++ b/src/jalview/gui/Finder.java @@ -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 idMatch = finder.getIdMatch(); boolean haveResults = false; // set or reset the GUI if ((idMatch.size() > 0)) diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index e321a74..6a20872 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -109,8 +109,8 @@ public class IdPanel extends JPanel implements MouseListener, if (seq > -1 && seq < av.getAlignment().getHeight()) { SequenceI sequence = av.getAlignment().getSequenceAt(seq); - StringBuffer tip = new StringBuffer(64); - seqAnnotReport.createSequenceAnnotationReport(tip, sequence, + StringBuilder tip = new StringBuilder(64); + seqAnnotReport.createTooltipAnnotationReport(tip, sequence, av.isShowDBRefs(), av.isShowNPFeats(), sp.seqCanvas.fr.getMinMax()); setToolTipText(JvSwingUtils.wrapTooltip(true, diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index bb816cd..5825382 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -1677,7 +1677,7 @@ public class PopupMenu extends JPopupMenu public void createSequenceDetailsReport(SequenceI[] sequences) { CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer(); - StringBuffer contents = new StringBuffer(); + StringBuilder contents = new StringBuilder(128); for (SequenceI seq : sequences) { contents.append("

    " @@ -1692,7 +1692,6 @@ public class PopupMenu extends JPopupMenu seq, true, true, - false, (ap.getSeqPanel().seqCanvas.fr != null) ? ap .getSeqPanel().seqCanvas.fr.getMinMax() : null); diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 760ece0..d015292 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -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(); } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 3266fab..36fb052 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -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; @@ -124,7 +125,7 @@ public class SeqPanel extends JPanel implements MouseListener, private final SequenceAnnotationReport seqARep; - StringBuffer tooltipText = new StringBuffer(); + StringBuilder tooltipText = new StringBuilder(); String tmpString; @@ -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)) { @@ -785,7 +786,7 @@ public class SeqPanel extends JPanel implements MouseListener, seqARep.appendFeatures(tooltipText, rpos, features, this.ap.getSeqPanel().seqCanvas.fr.getMinMax()); } - if (tooltipText.length() == 6) // + if (tooltipText.length() == 6) // { setToolTipText(null); lastTooltip = null; @@ -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); diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index 3350f6c..21b6e4f 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -83,6 +83,8 @@ public class StructureChooser extends GStructureChooser implements private boolean cachedPDBExists; + private static int MAX_QLENGHT = 7820; + public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq, AlignmentPanel ap) { @@ -259,7 +261,8 @@ public class StructureChooser extends GStructureChooser implements StringBuilder queryBuilder = new StringBuilder(); Set seqRefs = new LinkedHashSet(); - if (seq.getAllPDBEntries() != null) + if (seq.getAllPDBEntries() != null + && queryBuilder.length() < MAX_QLENGHT) { for (PDBEntry entry : seq.getAllPDBEntries()) { @@ -268,7 +271,6 @@ public class StructureChooser extends GStructureChooser implements queryBuilder.append("pdb_id:") .append(entry.getId().toLowerCase()).append(" OR "); isPDBRefsFound = true; - // seqRefs.add(entry.getId()); } } } @@ -277,7 +279,8 @@ public class StructureChooser extends GStructureChooser implements { for (DBRefEntry dbRef : seq.getDBRefs()) { - if (isValidSeqName(getDBRefId(dbRef))) + if (isValidSeqName(getDBRefId(dbRef)) + && queryBuilder.length() < MAX_QLENGHT) { if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT)) { diff --git a/src/jalview/io/BioJsHTMLOutput.java b/src/jalview/io/BioJsHTMLOutput.java index 1be97f5..fd9c584 100644 --- a/src/jalview/io/BioJsHTMLOutput.java +++ b/src/jalview/io/BioJsHTMLOutput.java @@ -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(); + } + + } + } diff --git a/src/jalview/io/HTMLOutput.java b/src/jalview/io/HTMLOutput.java index ccf1ea4..d58bd67 100755 --- a/src/jalview/io/HTMLOutput.java +++ b/src/jalview/io/HTMLOutput.java @@ -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; @@ -43,8 +43,6 @@ public abstract class HTMLOutput protected IProgressIndicator pIndicator; - private boolean headless; - protected File generatedFile; public HTMLOutput(AlignmentPanel ap) @@ -56,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() - { - return true; - } - - @Override - public boolean isExportHiddenColumns() + exportSettings = new AlignExportSettingI() { - 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); @@ -252,7 +256,7 @@ public abstract class HTMLOutput public String getOutputFile() throws NoFileSelectedException { String selectedFile = null; - if (pIndicator != null && !headless) + if (pIndicator != null && !isHeadless()) { pIndicator.setProgressBar(MessageManager.formatMessage( "status.waiting_for_user_to_select_output_file", "HTML"), @@ -266,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); @@ -278,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; @@ -288,7 +289,7 @@ public abstract class HTMLOutput protected void setProgressMessage(String message) { - if (pIndicator != null && !headless) + if (pIndicator != null && !isHeadless()) { pIndicator.setProgressBar(message, pSessionId); } diff --git a/src/jalview/io/HtmlSvgOutput.java b/src/jalview/io/HtmlSvgOutput.java index e60824a..1ec3a4e 100644 --- a/src/jalview/io/HtmlSvgOutput.java +++ b/src/jalview/io/HtmlSvgOutput.java @@ -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(); } @@ -158,10 +81,12 @@ public class HtmlSvgOutput extends HTMLOutput "Hypertext Markup Language"); } - public int printUnwrapped(int pwidth, int pheight, int pi, Graphics... pg) + public int printUnwrapped(int pwidth, int pheight, int pi, + Graphics idGraphics, Graphics alignmentGraphics) throws PrinterException { - return ap.printUnwrapped(pwidth, pheight, pi, pg); + return ap.printUnwrapped(pwidth, pheight, pi, idGraphics, + alignmentGraphics); } public int printWrapped(int pwidth, int pheight, int pi, Graphics... pg) @@ -241,11 +166,9 @@ public class HtmlSvgOutput extends HTMLOutput .append(alignmentSvg).append("

    "); htmlSvg.append("\n" + "\n"); - } // javascript for launching file in Jalview - htmlSvg.append("